fortran-toml-0.5.0/0000775000175000017500000000000015201541503014320 5ustar alastairalastairfortran-toml-0.5.0/LICENSE-MIT0000664000175000017500000000204515201541453015761 0ustar alastairalastairCopyright 2019-2021 Sebastian Ehlert 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. fortran-toml-0.5.0/subprojects/0000775000175000017500000000000015201541453016667 5ustar alastairalastairfortran-toml-0.5.0/subprojects/toml-test-1.1.0.wrap0000664000175000017500000000042115201541453022142 0ustar alastairalastair[wrap-file] directory = toml-test-1.1.0 source_url = https://github.com/BurntSushi/toml-test/archive/refs/tags/v1.1.0.tar.gz source_filename = v1.1.0.tar.gz source_hash = dd6c681efd62a23593cb58fb8a81d6ab7d34d90a90f79e627f7361e3dac851c9 patch_directory = toml-test-1.1.0 fortran-toml-0.5.0/subprojects/packagefiles/0000775000175000017500000000000015201541453021305 5ustar alastairalastairfortran-toml-0.5.0/subprojects/packagefiles/toml-test-1.1.0/0000775000175000017500000000000015201541453023670 5ustar alastairalastairfortran-toml-0.5.0/subprojects/packagefiles/toml-test-1.1.0/tests/0000775000175000017500000000000015201541453025032 5ustar alastairalastairfortran-toml-0.5.0/subprojects/packagefiles/toml-test-1.1.0/tests/invalid-encoder/0000775000175000017500000000000015201541453030075 5ustar alastairalastairfortran-toml-0.5.0/subprojects/packagefiles/toml-test-1.1.0/tests/invalid-encoder/.gitkeep0000664000175000017500000000000015201541453031514 0ustar alastairalastairfortran-toml-0.5.0/subprojects/packagefiles/toml-test-1.1.0/meson.build0000664000175000017500000002536115201541453026041 0ustar alastairalastairproject( 'toml-test', ) go = find_program('go', required: false) if go.found() go_wrapper = find_program(files('go-wrapper.py')) toml_test_exe = find_program( configure_file( command: [ go_wrapper, meson.current_source_dir(), go, 'build', '-pkgdir', meson.current_build_dir(), '-o', '@OUTPUT@', '-v', '-x', './cmd/toml-test', ], output: 'toml-test', ), 'toml-test', required: false, ) else toml_test_exe = disabler() endif compliance_tests = meson.current_source_dir() / 'tests' valid_tests = [ 'valid/array/array', 'valid/array/bool', 'valid/array/empty', 'valid/array/hetergeneous', 'valid/array/mixed-int-array', 'valid/array/mixed-int-float', 'valid/array/mixed-int-string', 'valid/array/mixed-string-table', 'valid/array/nested-double', 'valid/array/nested-inline-table', 'valid/array/nested', 'valid/array/nospaces', 'valid/array/string-quote-comma-2', 'valid/array/string-quote-comma', 'valid/array/string-with-comma', 'valid/array/strings', 'valid/array/table-array-string-backslash', 'valid/bool/bool', 'valid/comment/at-eof', 'valid/comment/at-eof2', 'valid/comment/everywhere', 'valid/comment/noeol', 'valid/comment/tricky', 'valid/datetime/datetime', 'valid/datetime/local-date', 'valid/datetime/local-time', 'valid/datetime/local', 'valid/datetime/milliseconds', 'valid/datetime/timezone', 'valid/empty-file', 'valid/example', 'valid/float/exponent', 'valid/float/float', 'valid/float/inf-and-nan', 'valid/float/long', 'valid/float/underscore', 'valid/float/zero', 'valid/implicit-and-explicit-after', 'valid/implicit-and-explicit-before', 'valid/implicit-groups', 'valid/inline-table/array', 'valid/inline-table/bool', 'valid/inline-table/empty', 'valid/inline-table/end-in-bool', 'valid/inline-table/inline-table', 'valid/inline-table/key-dotted', 'valid/inline-table/multiline', 'valid/inline-table/nest', 'valid/integer/integer', 'valid/integer/literals', 'valid/integer/long', 'valid/integer/underscore', 'valid/integer/zero', 'valid/key/alphanum', 'valid/key/case-sensitive', 'valid/key/dotted', 'valid/key/empty', 'valid/key/equals-nospace', 'valid/key/escapes', 'valid/key/numeric-dotted', 'valid/key/numeric', 'valid/key/quoted-dots', 'valid/key/space', 'valid/key/special-chars', 'valid/key/special-word', 'valid/newline-crlf', 'valid/newline-lf', 'valid/spec-example-1-compact', 'valid/spec-example-1', 'valid/string/double-quote-escape', 'valid/string/empty', 'valid/string/escape-tricky', 'valid/string/escaped-escape', 'valid/string/escapes', 'valid/string/multiline-escaped-crlf', 'valid/string/multiline-quotes', 'valid/string/multiline', 'valid/string/nl', 'valid/string/raw-multiline', 'valid/string/raw', 'valid/string/simple', 'valid/string/unicode-escape', 'valid/string/unicode-literal', 'valid/string/with-pound', 'valid/table/array-implicit', 'valid/table/array-many', 'valid/table/array-nest', 'valid/table/array-one', 'valid/table/array-table-array', 'valid/table/empty', 'valid/table/keyword', 'valid/table/names', 'valid/table/no-eol', 'valid/table/sub-empty', 'valid/table/whitespace', 'valid/table/with-literal-string', 'valid/table/with-pound', 'valid/table/with-single-quotes', 'valid/table/without-super', ] invalid_tests = [ 'invalid/array/missing-separator', 'invalid/array/no-close-2', 'invalid/array/no-close-table-2', 'invalid/array/no-close-table', 'invalid/array/no-close', 'invalid/array/tables-1', 'invalid/array/tables-2', 'invalid/array/text-after-array-entries', 'invalid/array/text-before-array-separator', 'invalid/array/text-in-array', 'invalid/bool/mixed-case', 'invalid/bool/wrong-case-false', 'invalid/bool/wrong-case-true', 'invalid/control/bare-cr', 'invalid/control/bare-formfeed', 'invalid/control/bare-null', 'invalid/control/bare-vertical-tab', 'invalid/control/comment-cr', 'invalid/control/comment-del', 'invalid/control/comment-lf', 'invalid/control/comment-null', 'invalid/control/comment-us', 'invalid/control/multi-del', 'invalid/control/multi-lf', 'invalid/control/multi-null', 'invalid/control/multi-us', 'invalid/control/rawmulti-del', 'invalid/control/rawmulti-lf', 'invalid/control/rawmulti-null', 'invalid/control/rawmulti-us', 'invalid/control/rawstring-del', 'invalid/control/rawstring-lf', 'invalid/control/rawstring-null', 'invalid/control/rawstring-us', 'invalid/control/string-bs', 'invalid/control/string-del', 'invalid/control/string-lf', 'invalid/control/string-null', 'invalid/control/string-us', 'invalid/datetime/hour-over', 'invalid/datetime/mday-over', 'invalid/datetime/mday-under', 'invalid/datetime/minute-over', 'invalid/datetime/month-over', 'invalid/datetime/month-under', 'invalid/datetime/no-leads-with-milli', 'invalid/datetime/no-leads', 'invalid/datetime/no-secs', 'invalid/datetime/no-t', 'invalid/datetime/second-over', 'invalid/datetime/time-no-leads-2', 'invalid/datetime/time-no-leads', 'invalid/datetime/trailing-t', 'invalid/encoding/bad-utf8-at-end', 'invalid/encoding/bad-utf8-in-comment', 'invalid/encoding/bad-utf8-in-string', 'invalid/encoding/bom-not-at-start-1', 'invalid/encoding/bom-not-at-start-2', 'invalid/encoding/utf16-bom', 'invalid/encoding/utf16', 'invalid/float/double-point-1', 'invalid/float/double-point-2', 'invalid/float/exp-double-e-1', 'invalid/float/exp-double-e-2', 'invalid/float/exp-double-us', 'invalid/float/exp-leading-us', 'invalid/float/exp-point-1', 'invalid/float/exp-point-2', 'invalid/float/exp-trailing-us', 'invalid/float/inf-incomplete-1', 'invalid/float/inf-incomplete-2', 'invalid/float/inf-incomplete-3', 'invalid/float/inf_underscore', 'invalid/float/leading-point-neg', 'invalid/float/leading-point-plus', 'invalid/float/leading-point', 'invalid/float/leading-us', 'invalid/float/leading-zero-neg', 'invalid/float/leading-zero-plus', 'invalid/float/leading-zero', 'invalid/float/nan-incomplete-1', 'invalid/float/nan-incomplete-2', 'invalid/float/nan-incomplete-3', 'invalid/float/nan_underscore', 'invalid/float/trailing-point-min', 'invalid/float/trailing-point-plus', 'invalid/float/trailing-point', 'invalid/float/trailing-us-exp', 'invalid/float/trailing-us', 'invalid/float/us-after-point', 'invalid/float/us-before-point', 'invalid/inline-table/add', 'invalid/inline-table/double-comma', 'invalid/inline-table/duplicate-key', 'invalid/inline-table/empty', 'invalid/inline-table/linebreak-1', 'invalid/inline-table/linebreak-2', 'invalid/inline-table/linebreak-3', 'invalid/inline-table/linebreak-4', 'invalid/inline-table/no-comma', 'invalid/inline-table/overwrite', 'invalid/inline-table/trailing-comma', 'invalid/integer/capital-bin', 'invalid/integer/capital-hex', 'invalid/integer/capital-oct', 'invalid/integer/double-sign-nex', 'invalid/integer/double-sign-plus', 'invalid/integer/double-us', 'invalid/integer/incomplete-bin', 'invalid/integer/incomplete-hex', 'invalid/integer/incomplete-oct', 'invalid/integer/invalid-bin', 'invalid/integer/invalid-hex', 'invalid/integer/invalid-oct', 'invalid/integer/leading-us-bin', 'invalid/integer/leading-us-hex', 'invalid/integer/leading-us-oct', 'invalid/integer/leading-us', 'invalid/integer/leading-zero-1', 'invalid/integer/leading-zero-2', 'invalid/integer/leading-zero-3', 'invalid/integer/leading-zero-sign-1', 'invalid/integer/leading-zero-sign-2', 'invalid/integer/leading-zero-sign-3', 'invalid/integer/negative-bin', 'invalid/integer/negative-hex', 'invalid/integer/negative-oct', 'invalid/integer/positive-bin', 'invalid/integer/positive-hex', 'invalid/integer/positive-oct', 'invalid/integer/text-after-integer', 'invalid/integer/trailing-us-bin', 'invalid/integer/trailing-us-hex', 'invalid/integer/trailing-us-oct', 'invalid/integer/trailing-us', 'invalid/integer/us-after-bin', 'invalid/integer/us-after-hex', 'invalid/integer/us-after-oct', 'invalid/key/after-array', 'invalid/key/after-table', 'invalid/key/after-value', 'invalid/key/bare-invalid-character', 'invalid/key/dotted-redefine-table', 'invalid/key/duplicate-keys', 'invalid/key/duplicate', 'invalid/key/empty', 'invalid/key/escape', 'invalid/key/hash', 'invalid/key/multiline', 'invalid/key/newline', 'invalid/key/no-eol', 'invalid/key/open-bracket', 'invalid/key/partial-quoted', 'invalid/key/single-open-bracket', 'invalid/key/space', 'invalid/key/special-character', 'invalid/key/start-bracket', 'invalid/key/two-equals', 'invalid/key/two-equals2', 'invalid/key/two-equals3', 'invalid/key/without-value-1', 'invalid/key/without-value-2', 'invalid/string/bad-byte-escape', 'invalid/string/bad-codepoint', 'invalid/string/bad-concat', 'invalid/string/bad-escape-1', 'invalid/string/bad-escape-2', 'invalid/string/bad-multiline', 'invalid/string/bad-slash-escape', 'invalid/string/bad-uni-esc', 'invalid/string/basic-byte-escapes', 'invalid/string/basic-multiline-out-of-range-unicode-escape-1', 'invalid/string/basic-multiline-out-of-range-unicode-escape-2', 'invalid/string/basic-multiline-quotes', 'invalid/string/basic-multiline-unknown-escape', 'invalid/string/basic-out-of-range-unicode-escape-1', 'invalid/string/basic-out-of-range-unicode-escape-2', 'invalid/string/basic-unknown-escape', 'invalid/string/literal-multiline-quotes-1', 'invalid/string/literal-multiline-quotes-2', 'invalid/string/missing-quotes', 'invalid/string/multiline-bad-escape-1', 'invalid/string/multiline-bad-escape-2', 'invalid/string/multiline-bad-escape-3', 'invalid/string/multiline-escape-space', 'invalid/string/multiline-no-close-2', 'invalid/string/multiline-no-close', 'invalid/string/multiline-quotes-1', 'invalid/string/no-close', 'invalid/string/text-after-string', 'invalid/string/wrong-close', 'invalid/table/append-with-dotted-keys-1', 'invalid/table/append-with-dotted-keys-2', 'invalid/table/array-empty', 'invalid/table/array-implicit', 'invalid/table/array-missing-bracket', 'invalid/table/duplicate-key-dotted-table', 'invalid/table/duplicate-key-dotted-table2', 'invalid/table/duplicate-key-table', 'invalid/table/duplicate-table-array', 'invalid/table/duplicate-table-array2', 'invalid/table/duplicate', 'invalid/table/empty-implicit-table', 'invalid/table/empty', 'invalid/table/equals-sign', 'invalid/table/llbrace', 'invalid/table/nested-brackets-close', 'invalid/table/nested-brackets-open', 'invalid/table/quoted-no-close', 'invalid/table/redefine', 'invalid/table/rrbrace', 'invalid/table/text-after-table', 'invalid/table/whitespace', 'invalid/table/with-pound', ] fortran-toml-0.5.0/subprojects/packagefiles/toml-test-1.1.0/go-wrapper.py0000664000175000017500000000036415201541453026330 0ustar alastairalastair#!/usr/bin/env python3 if __name__ == "__main__": import sys, subprocess out, err = subprocess.Popen( sys.argv[2:], cwd=sys.argv[1], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ).communicate() fortran-toml-0.5.0/subprojects/test-drive.wrap0000664000175000017500000000015115201541453021645 0ustar alastairalastair[wrap-git] directory = test-drive url = https://github.com/fortran-lang/test-drive.git revision = v0.4.0 fortran-toml-0.5.0/subprojects/.gitignore0000664000175000017500000000001515201541453020653 0ustar alastairalastair/toml-tests/ fortran-toml-0.5.0/src/0000775000175000017500000000000015201541453015113 5ustar alastairalastairfortran-toml-0.5.0/src/tomlf.f900000664000175000017500000000431115201541453016553 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Public API for TOML Fortran !> !> This module provides the main entry point to the TOML Fortran library. !> It re-exports all public types and procedures needed for parsing, manipulating, !> and serializing TOML documents. !> !> ## Parsing TOML !> !> Use [[toml_load]] to load a TOML document from a file or unit, or [[toml_loads]] !> to parse a TOML string directly: !> !>```fortran !> type(toml_table), allocatable :: table !> call toml_load(table, "config.toml") !>``` !> !> ## Accessing Values !> !> Use [[get_value]] to retrieve values from tables and arrays, and [[set_value]] !> to modify or create new values: !> !>```fortran !> character(len=:), allocatable :: name !> call get_value(table, "name", name) !>``` !> !> ## Serialization !> !> Use [[toml_dump]] to write a table to a file or [[toml_dumps]] to serialize !> to a string: !> !>```fortran !> character(len=:), allocatable :: output !> call toml_dumps(table, output) !>``` module tomlf use tomlf_build, only : get_value, set_value, toml_path use tomlf_datetime, only : toml_datetime, to_string use tomlf_de, only : toml_parse, toml_load, toml_loads, & & toml_context, toml_parser_config, toml_level use tomlf_error, only : toml_error, toml_stat use tomlf_ser, only : toml_serializer, toml_serialize, toml_dump, toml_dumps use tomlf_terminal, only : toml_terminal use tomlf_type, only : toml_table, toml_array, toml_keyval, toml_key, toml_value, & & is_array_of_tables, new_table, add_table, add_array, add_keyval, len use tomlf_utils_sort, only : sort use tomlf_version, only : tomlf_version_string, tomlf_version_compact, & & get_tomlf_version implicit none public end module tomlf fortran-toml-0.5.0/src/meson.build0000664000175000017500000000112115201541453017250 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. subdir('tomlf') srcs += files( 'tomlf.f90', ) fortran-toml-0.5.0/src/tomlf/0000775000175000017500000000000015201541453016234 5ustar alastairalastairfortran-toml-0.5.0/src/tomlf/version.f900000664000175000017500000000423015201541453020240 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Version information on TOML-Fortran module tomlf_version implicit none private public :: get_tomlf_version public :: tomlf_version_string, tomlf_version_compact !> String representation of the TOML-Fortran version character(len=*), parameter :: tomlf_version_string = "0.5.0" !> Major version number of the above TOML-Fortran version integer, parameter :: tomlf_major = 0 !> Minor version number of the above TOML-Fortran version integer, parameter :: tomlf_minor = 5 !> Patch version number of the above TOML-Fortran version integer, parameter :: tomlf_patch = 0 !> Compact numeric representation of the TOML-Fortran version integer, parameter :: tomlf_version_compact = & & tomlf_major*10000 + tomlf_minor*100 + tomlf_patch contains !> Getter function to retrieve TOML-Fortran version subroutine get_tomlf_version(major, minor, patch, string) !> Major version number of the TOML-Fortran version integer, intent(out), optional :: major !> Minor version number of the TOML-Fortran version integer, intent(out), optional :: minor !> Patch version number of the TOML-Fortran version integer, intent(out), optional :: patch !> String representation of the TOML-Fortran version character(len=:), allocatable, intent(out), optional :: string if (present(major)) then major = tomlf_major end if if (present(minor)) then minor = tomlf_minor end if if (present(patch)) then patch = tomlf_patch end if if (present(string)) then string = tomlf_version_string end if end subroutine get_tomlf_version end module tomlf_version fortran-toml-0.5.0/src/tomlf/terminal.f900000664000175000017500000002557515201541453020405 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of a terminal to provide ANSI escape sequences !> !> ANSI escape codes for producing terminal colors. The `ansi_code` derived !> type is used to store ANSI escape codes and can be combined with other !> codes or applied to strings by concatenation. The default or uninitialized !> `ansi_code` is a stub and does not produce escape sequences when applied !> to a string. !> !> Available colors are !> !> color | foreground | background !> -------------- | --------------------- | ------------------------ !> black | `black` (30) | `bg_black` (40) !> red | `red` (31) | `bg_red` (41) !> green | `green` (32) | `bg_green` (42) !> yellow | `yellow` (33) | `bg_yellow` (43) !> blue | `blue` (34) | `bg_blue` (44) !> magenta | `magenta` (35) | `bg_magenta` (45) !> cyan | `cyan` (36) | `bg_cyan` (46) !> white | `white` (37) | `bg_white` (47) !> gray | `gray` (90) | `bg_gray` (100) !> bright red | `bright_red` (91) | `bg_bright_red` (101) !> bright green | `bright_green` (92) | `bg_bright_green` (102) !> bright yellow | `bright_yellow` (93) | `bg_bright_yellow` (103) !> bright blue | `bright_blue` (94) | `bg_bright_blue` (104) !> bright magenta | `bright_magenta` (95) | `bg_bright_magenta` (105) !> bright cyan | `bright_cyan` (96) | `bg_bright_cyan` (106) !> bright white | `bright_white` (97) | `bg_bright_white` (107) !> !> Available styles are !> !> style | !> ------------| --------------- !> reset | `reset` (0) !> bold | `bold` (1) !> dim | `dim` (2) !> italic | `italic` (3) !> underline | `underline` (4) !> blink | `blink` (5) !> blink rapid | `blink_rapid` (6) !> reverse | `reverse` (7) !> hidden | `hidden` (8) !> crossed | `crossed` (9) module tomlf_terminal use tomlf_utils, only : to_string implicit none private public :: toml_terminal public :: ansi_code, escape, operator(+), operator(//) !> Char length for integers integer, parameter :: i1 = selected_int_kind(2) !> Container for terminal escape code type :: ansi_code private !> Style descriptor integer(i1) :: style = -1_i1 !> Background color descriptor integer(i1) :: bg = -1_i1 !> Foreground color descriptor integer(i1) :: fg = -1_i1 end type interface operator(+) module procedure :: add end interface operator(+) interface operator(//) module procedure :: concat_left module procedure :: concat_right end interface operator(//) interface escape module procedure :: escape end interface escape type(ansi_code), public, parameter :: & reset = ansi_code(style=0_i1), & bold = ansi_code(style=1_i1), & dim = ansi_code(style=2_i1), & italic = ansi_code(style=3_i1), & underline = ansi_code(style=4_i1), & blink = ansi_code(style=5_i1), & blink_rapid = ansi_code(style=6_i1), & reverse = ansi_code(style=7_i1), & hidden = ansi_code(style=8_i1), & crossed = ansi_code(style=9_i1) type(ansi_code), public, parameter :: & black = ansi_code(fg=30_i1), & red = ansi_code(fg=31_i1), & green = ansi_code(fg=32_i1), & yellow = ansi_code(fg=33_i1), & blue = ansi_code(fg=34_i1), & magenta = ansi_code(fg=35_i1), & cyan = ansi_code(fg=36_i1), & white = ansi_code(fg=37_i1), & gray = ansi_code(fg=90_i1), & bright_red = ansi_code(fg=91_i1), & bright_green = ansi_code(fg=92_i1), & bright_yellow = ansi_code(fg=93_i1), & bright_blue = ansi_code(fg=94_i1), & bright_magenta = ansi_code(fg=95_i1), & bright_cyan = ansi_code(fg=96_i1), & bright_white = ansi_code(fg=97_i1) type(ansi_code), public, parameter :: & bg_black = ansi_code(bg=40_i1), & bg_red = ansi_code(bg=41_i1), & bg_green = ansi_code(bg=42_i1), & bg_yellow = ansi_code(bg=43_i1), & bg_blue = ansi_code(bg=44_i1), & bg_magenta = ansi_code(bg=45_i1), & bg_cyan = ansi_code(bg=46_i1), & bg_white = ansi_code(bg=47_i1), & bg_gray = ansi_code(bg=100_i1), & bg_bright_red = ansi_code(bg=101_i1), & bg_bright_green = ansi_code(bg=102_i1), & bg_bright_yellow = ansi_code(bg=103_i1), & bg_bright_blue = ansi_code(bg=104_i1), & bg_bright_magenta = ansi_code(bg=105_i1), & bg_bright_cyan = ansi_code(bg=106_i1), & bg_bright_white = ansi_code(bg=107_i1) !> Terminal wrapper to handle color escape sequences, must be initialized with !> color support to provide colorful output. Default and uninitialized instances !> will remain usable but provide only stubs and do not produce colorful output. !> This behavior is useful for creating applications which can toggle color support. type :: toml_terminal type(ansi_code) :: & reset = ansi_code(), & bold = ansi_code(), & dim = ansi_code(), & italic = ansi_code(), & underline = ansi_code(), & blink = ansi_code(), & blink_rapid = ansi_code(), & reverse = ansi_code(), & hidden = ansi_code(), & crossed = ansi_code() type(ansi_code) :: & black = ansi_code(), & red = ansi_code(), & green = ansi_code(), & yellow = ansi_code(), & blue = ansi_code(), & magenta = ansi_code(), & cyan = ansi_code(), & white = ansi_code(), & gray = ansi_code(), & bright_red = ansi_code(), & bright_green = ansi_code(), & bright_yellow = ansi_code(), & bright_blue = ansi_code(), & bright_magenta = ansi_code(), & bright_cyan = ansi_code(), & bright_white = ansi_code() type(ansi_code) :: & bg_black = ansi_code(), & bg_red = ansi_code(), & bg_green = ansi_code(), & bg_yellow = ansi_code(), & bg_blue = ansi_code(), & bg_magenta = ansi_code(), & bg_cyan = ansi_code(), & bg_white = ansi_code(), & bg_gray = ansi_code(), & bg_bright_red = ansi_code(), & bg_bright_green = ansi_code(), & bg_bright_yellow = ansi_code(), & bg_bright_blue = ansi_code(), & bg_bright_magenta = ansi_code(), & bg_bright_cyan = ansi_code(), & bg_bright_white = ansi_code() end type toml_terminal !> Constructor to create new terminal interface toml_terminal module procedure :: new_terminal end interface toml_terminal contains !> Create new terminal pure function new_terminal(use_color) result(new) !> Enable color support in terminal logical, intent(in) :: use_color !> New terminal instance type(toml_terminal) :: new if (use_color) then new%reset = reset new%bold = bold new%dim = dim new%italic = italic new%underline = underline new%blink = blink new%blink_rapid = blink_rapid new%reverse = reverse new%hidden = hidden new%crossed = crossed new%black = black new%red = red new%green = green new%yellow = yellow new%blue = blue new%magenta = magenta new%cyan = cyan new%white = white new%gray = gray new%bright_red = bright_red new%bright_green = bright_green new%bright_yellow = bright_yellow new%bright_blue = bright_blue new%bright_magenta = bright_magenta new%bright_cyan = bright_cyan new%bright_white = bright_white new%bg_black = bg_black new%bg_red = bg_red new%bg_green = bg_green new%bg_yellow = bg_yellow new%bg_blue = bg_blue new%bg_magenta = bg_magenta new%bg_cyan = bg_cyan new%bg_white = bg_white new%bg_gray = bg_gray new%bg_bright_red = bg_bright_red new%bg_bright_green = bg_bright_green new%bg_bright_yellow = bg_bright_yellow new%bg_bright_blue = bg_bright_blue new%bg_bright_magenta = bg_bright_magenta new%bg_bright_cyan = bg_bright_cyan new%bg_bright_white = bg_bright_white end if end function new_terminal !> Add two escape sequences, attributes in the right value override the left value ones. pure function add(lval, rval) result(code) !> First escape code type(ansi_code), intent(in) :: lval !> Second escape code type(ansi_code), intent(in) :: rval !> Combined escape code type(ansi_code) :: code code%style = merge(rval%style, lval%style, rval%style >= 0) code%fg = merge(rval%fg, lval%fg, rval%fg >= 0) code%bg = merge(rval%bg, lval%bg, rval%bg >= 0) end function add !> Concatenate an escape code with a string and turn it into an actual escape sequence pure function concat_left(lval, code) result(str) !> String to add the escape code to character(len=*), intent(in) :: lval !> Escape sequence type(ansi_code), intent(in) :: code !> Concatenated string character(len=:), allocatable :: str str = lval // escape(code) end function concat_left !> Concatenate an escape code with a string and turn it into an actual escape sequence pure function concat_right(code, rval) result(str) !> String to add the escape code to character(len=*), intent(in) :: rval !> Escape sequence type(ansi_code), intent(in) :: code !> Concatenated string character(len=:), allocatable :: str str = escape(code) // rval end function concat_right !> Transform a color code into an actual ANSI escape sequence pure function escape(code) result(str) !> Color code to be used type(ansi_code), intent(in) :: code !> ANSI escape sequence representing the color code character(len=:), allocatable :: str if (anycolor(code)) then str = achar(27) // "[0" ! Always reset the style if (code%style > 0) str = str // ";" // to_string(code%style) if (code%fg >= 0) str = str // ";" // to_string(code%fg) if (code%bg >= 0) str = str // ";" // to_string(code%bg) str = str // "m" else str = "" end if end function escape !> Check whether the code describes any color or is just a stub pure function anycolor(code) !> Escape sequence type(ansi_code), intent(in) :: code !> Any color / style is active logical :: anycolor anycolor = code%fg >= 0 .or. code%bg >= 0 .or. code%style >= 0 end function anycolor end module tomlf_terminal fortran-toml-0.5.0/src/tomlf/type.f900000664000175000017500000003320315201541453017536 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Collection of the central datatypes to define TOML data structures !> !> All TOML data types should inherit from an abstract value allowing to generate !> a generic interface to deal with all more specialized TOML data types, while !> the abstract value is interesting for developing algorithms in TOML-Fortran, !> the user of TOML-Fortran will usually only care about TOML tables and possibly !> arrays. !> !> The TOML types defined here should implement the TOML data structures (mostly) !> without taking the actual implementation of the data structures into account. !> This is done by providing a bare minimum interface using type bound procedures !> to minimize the interdependencies between the datatypes. !> !> To make the data types extendable a visitor pattern allows access to the TOML !> data types and can be used to implement further algorithms. module tomlf_type use tomlf_constants, only : tfc use tomlf_error, only : toml_stat use tomlf_type_array, only : toml_array, new_array, new, initialized, len use tomlf_type_keyval, only : toml_keyval, new_keyval, new use tomlf_type_table, only : toml_table, new_table, new, initialized use tomlf_type_value, only : toml_value, toml_visitor, toml_key implicit none private public :: toml_value, toml_visitor, toml_table, toml_array, toml_keyval public :: toml_key public :: new, new_table, new_array, new_keyval, initialized, len public :: add_table, add_array, add_keyval public :: is_array_of_tables public :: cast_to_table, cast_to_array, cast_to_keyval !> Interface to build new tables interface add_table module procedure :: add_table_to_table module procedure :: add_table_to_table_key module procedure :: add_table_to_array end interface add_table !> Interface to build new arrays interface add_array module procedure :: add_array_to_table module procedure :: add_array_to_table_key module procedure :: add_array_to_array end interface add_array !> Interface to build new key-value pairs interface add_keyval module procedure :: add_keyval_to_table module procedure :: add_keyval_to_table_key module procedure :: add_keyval_to_array end interface add_keyval contains !> Create a new TOML table inside an existing table subroutine add_table_to_table(table, key, ptr, stat) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key for the new table character(kind=tfc, len=*), intent(in) :: key !> Pointer to the newly created table type(toml_table), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat class(toml_value), allocatable :: val class(toml_value), pointer :: tmp integer :: istat nullify(ptr) call new_table_(val) val%key = key call table%push_back(val, istat) if (allocated(val)) then call val%destroy if (present(stat)) stat = toml_stat%fatal return end if if (istat == toml_stat%success) then call table%get(key, tmp) if (.not.associated(tmp)) then if (present(stat)) stat = toml_stat%fatal return end if select type(tmp) type is(toml_table) ptr => tmp class default istat = toml_stat%fatal end select end if if (present(stat)) stat = istat end subroutine add_table_to_table !> Create a new TOML table inside an existing table subroutine add_table_to_table_key(table, key, ptr, stat) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key for the new table type(toml_key), intent(in) :: key !> Pointer to the newly created table type(toml_table), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat call add_table(table, key%key, ptr, stat) if (associated(ptr)) ptr%origin = key%origin end subroutine add_table_to_table_key !> Create a new TOML array inside an existing table subroutine add_array_to_table(table, key, ptr, stat) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key for the new array character(kind=tfc, len=*), intent(in) :: key !> Pointer to the newly created array type(toml_array), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat class(toml_value), allocatable :: val class(toml_value), pointer :: tmp integer :: istat nullify(ptr) call new_array_(val) val%key = key call table%push_back(val, istat) if (allocated(val)) then call val%destroy if (present(stat)) stat = toml_stat%fatal return end if if (istat == toml_stat%success) then call table%get(key, tmp) if (.not.associated(tmp)) then if (present(stat)) stat = toml_stat%fatal return end if select type(tmp) type is(toml_array) ptr => tmp class default istat = toml_stat%fatal end select end if if (present(stat)) stat = istat end subroutine add_array_to_table !> Create a new TOML array inside an existing table subroutine add_array_to_table_key(table, key, ptr, stat) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key for the new array type(toml_key), intent(in) :: key !> Pointer to the newly created array type(toml_array), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat call add_array(table, key%key, ptr, stat) if (associated(ptr)) ptr%origin = key%origin end subroutine add_array_to_table_key !> Create a new key-value pair inside an existing table subroutine add_keyval_to_table(table, key, ptr, stat) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key for the new key-value pair character(kind=tfc, len=*), intent(in) :: key !> Pointer to the newly created key-value pair type(toml_keyval), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat class(toml_value), allocatable :: val class(toml_value), pointer :: tmp integer :: istat nullify(ptr) call new_keyval_(val) val%key = key call table%push_back(val, istat) if (allocated(val)) then call val%destroy if (present(stat)) stat = toml_stat%fatal return end if if (istat == toml_stat%success) then call table%get(key, tmp) if (.not.associated(tmp)) then if (present(stat)) stat = toml_stat%fatal return end if select type(tmp) type is(toml_keyval) ptr => tmp class default istat = toml_stat%fatal end select end if if (present(stat)) stat = istat end subroutine add_keyval_to_table !> Create a new key-value pair inside an existing table subroutine add_keyval_to_table_key(table, key, ptr, stat) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key for the new key-value pair type(toml_key), intent(in) :: key !> Pointer to the newly created key-value pair type(toml_keyval), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat call add_keyval(table, key%key, ptr, stat) if (associated(ptr)) ptr%origin = key%origin end subroutine add_keyval_to_table_key !> Create a new TOML table inside an existing array subroutine add_table_to_array(array, ptr, stat) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Pointer to the newly created table type(toml_table), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat class(toml_value), allocatable :: val class(toml_value), pointer :: tmp integer :: istat nullify(ptr) call new_table_(val) call array%push_back(val, istat) if (allocated(val)) then call val%destroy if (present(stat)) stat = toml_stat%fatal return end if if (istat == toml_stat%success) then call array%get(len(array), tmp) if (.not.associated(tmp)) then if (present(stat)) stat = toml_stat%fatal return end if select type(tmp) type is(toml_table) ptr => tmp class default istat = toml_stat%fatal end select end if if (present(stat)) stat = istat end subroutine add_table_to_array !> Create a new TOML array inside an existing array subroutine add_array_to_array(array, ptr, stat) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Pointer to the newly created array type(toml_array), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat class(toml_value), allocatable :: val class(toml_value), pointer :: tmp integer :: istat nullify(ptr) allocate(toml_array :: val) call new_array_(val) call array%push_back(val, istat) if (allocated(val)) then call val%destroy if (present(stat)) stat = toml_stat%fatal return end if if (istat == toml_stat%success) then call array%get(len(array), tmp) if (.not.associated(tmp)) then if (present(stat)) stat = toml_stat%fatal return end if select type(tmp) type is(toml_array) ptr => tmp class default istat = toml_stat%fatal end select end if if (present(stat)) stat = istat end subroutine add_array_to_array !> Create a new key-value pair inside an existing array subroutine add_keyval_to_array(array, ptr, stat) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Pointer to the newly created key-value pair type(toml_keyval), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat class(toml_value), allocatable :: val class(toml_value), pointer :: tmp integer :: istat nullify(ptr) call new_keyval_(val) call array%push_back(val, istat) if (allocated(val)) then call val%destroy if (present(stat)) stat = toml_stat%fatal return end if if (istat == toml_stat%success) then call array%get(len(array), tmp) if (.not.associated(tmp)) then if (present(stat)) stat = toml_stat%fatal return end if select type(tmp) type is(toml_keyval) ptr => tmp class default istat = toml_stat%fatal end select end if if (present(stat)) stat = istat end subroutine add_keyval_to_array !> Wrapped constructor to create a new TOML table on an abstract TOML value subroutine new_table_(self) !> Newly created TOML table class(toml_value), allocatable, intent(out) :: self type(toml_table), allocatable :: val allocate(val) call new_table(val) call move_alloc(val, self) end subroutine new_table_ !> Wrapped constructor to create a new TOML array on an abstract TOML value subroutine new_array_(self) !> Newly created TOML array class(toml_value), allocatable, intent(out) :: self type(toml_array), allocatable :: val allocate(val) call new_array(val) call move_alloc(val, self) end subroutine new_array_ !> Wrapped constructor to create a new TOML array on an abstract TOML value subroutine new_keyval_(self) !> Newly created key-value pair class(toml_value), allocatable, intent(out) :: self type(toml_keyval), allocatable :: val allocate(val) call new_keyval(val) call move_alloc(val, self) end subroutine new_keyval_ !> Determine if array contains only tables function is_array_of_tables(array) result(only_tables) !> TOML value to visit class(toml_array), intent(inout) :: array !> Array contains only tables logical :: only_tables class(toml_value), pointer :: ptr integer :: i, n n = len(array) only_tables = n > 0 do i = 1, n call array%get(i, ptr) select type(ptr) type is(toml_table) cycle class default only_tables = .false. exit end select end do end function is_array_of_tables !> Cast an abstract TOML value to a TOML array function cast_to_array(ptr) result(array) !> TOML value to be casted class(toml_value), intent(in), target :: ptr !> TOML array view, nullified if the value is not an array type(toml_array), pointer :: array nullify(array) select type(ptr) type is(toml_array) array => ptr end select end function cast_to_array !> Cast an abstract TOML value to a TOML table function cast_to_table(ptr) result(table) !> TOML value to be casted class(toml_value), intent(in), target :: ptr !> TOML table view, nullified if the value is not a table type(toml_table), pointer :: table nullify(table) select type(ptr) type is(toml_table) table => ptr end select end function cast_to_table !> Cast an abstract TOML value to a TOML key-value pair function cast_to_keyval(ptr) result(kval) !> TOML value to be casted class(toml_value), intent(in), target :: ptr !> TOML key-value view, nullified if the value is not a table type(toml_keyval), pointer :: kval nullify(kval) select type(ptr) type is(toml_keyval) kval => ptr end select end function cast_to_keyval end module tomlf_type fortran-toml-0.5.0/src/tomlf/build.f900000664000175000017500000000232615201541453017656 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Functions to build a TOML data structures !> !> The build module defines a high level interface to work with TOML data structures !> and construct them in a convenient way. module tomlf_build use tomlf_build_array, only : get_value, set_value use tomlf_build_keyval, only : get_value, set_value use tomlf_build_merge, only : merge_table, merge_array, merge_policy, toml_merge_config use tomlf_build_path, only : get_value, set_value, toml_path use tomlf_build_table, only : get_value, set_value implicit none private public :: get_value, set_value public :: merge_table, merge_array, merge_policy, toml_merge_config public :: toml_path end module tomlf_build fortran-toml-0.5.0/src/tomlf/diagnostic.f900000664000175000017500000003357115201541453020711 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Diagnostic message support for TOML Fortran module tomlf_diagnostic use tomlf_terminal, only : toml_terminal, ansi_code, operator(//), operator(+) implicit none private public :: render public :: toml_diagnostic, toml_label interface render module procedure render_diagnostic module procedure render_text module procedure render_text_with_label module procedure render_text_with_labels end interface render !> Enumerator for diagnostic levels type :: level_enum integer :: error = 0 integer :: warning = 1 integer :: help = 2 integer :: note = 3 integer :: info = 4 end type level_enum !> Actual enumerator values type(level_enum), parameter, public :: toml_level = level_enum() type toml_label !> Level of message integer :: level !> Primary message logical :: primary !> First and last character of message integer :: first, last !> Message text character(len=:), allocatable :: text !> Identifier of context character(len=:), allocatable :: source end type toml_label interface toml_label module procedure new_label end interface toml_label !> Definition of diagnostic message type :: toml_diagnostic !> Level of message integer :: level !> Primary message character(len=:), allocatable :: message !> Context of the diagnostic source character(len=:), allocatable :: source !> Messages associated with this diagnostic type(toml_label), allocatable :: label(:) end type toml_diagnostic interface toml_diagnostic module procedure new_diagnostic end interface toml_diagnostic type :: line_token integer :: first, last end type line_token character(len=*), parameter :: nl = new_line('a') contains pure function new_label(level, first, last, text, primary) result(new) integer, intent(in) :: level integer, intent(in) :: first, last character(len=*), intent(in), optional :: text logical, intent(in), optional :: primary type(toml_label) :: new if (present(text)) new%text = text new%level = level new%first = first new%last = last if (present(primary)) then new%primary = primary else new%primary = .false. end if end function new_label !> Create new diagnostic message pure function new_diagnostic(level, message, source, label) result(new) !> Level of message integer, intent(in) :: level !> Primary message character(len=*), intent(in), optional :: message !> Context of the diagnostic source character(len=*), intent(in), optional :: source !> Messages associated with this diagnostic type(toml_label), intent(in), optional :: label(:) type(toml_diagnostic) :: new new%level = level if (present(message)) new%message = message if (present(source)) new%source = source if (present(label)) new%label = label end function new_diagnostic pure function line_tokens(input) result(token) character(len=*), intent(in) :: input type(line_token), allocatable :: token(:) integer :: first, last first = 1 last = 1 allocate(token(0)) do while (first <= len(input)) if (input(last:last) /= nl) then last = last + 1 cycle end if token = [token, line_token(first, last-1)] first = last + 1 last = first end do end function line_tokens recursive pure function render_diagnostic(diag, input, color) result(string) character(len=*), intent(in) :: input type(toml_diagnostic), intent(in) :: diag type(toml_terminal), intent(in) :: color character(len=:), allocatable :: string string = & render_message(diag%level, diag%message, color) if (allocated(diag%label)) then string = string // nl // & render_text_with_labels(input, diag%label, color, source=diag%source) end if end function render_diagnostic pure function render_message(level, message, color) result(string) integer, intent(in) :: level character(len=*), intent(in), optional :: message type(toml_terminal), intent(in) :: color character(len=:), allocatable :: string if (present(message)) then string = & level_name(level, color) // color%bold // ": " // message // color%reset else string = & level_name(level, color) end if end function render_message pure function level_name(level, color) result(string) integer, intent(in) :: level type(toml_terminal), intent(in) :: color character(len=:), allocatable :: string select case(level) case(toml_level%error) string = color%bold + color%red // "error" // color%reset case(toml_level%warning) string = color%bold + color%yellow // "warning" // color%reset case(toml_level%help) string = color%bold + color%cyan // "help" // color%reset case(toml_level%note) string = color%bold + color%blue // "note" // color%reset case(toml_level%info) string = color%bold + color%magenta // "info" // color%reset case default string = color%bold + color%blue // "unknown" // color%reset end select end function level_name pure function render_source(source, offset, color) result(string) character(len=*), intent(in) :: source integer, intent(in) :: offset type(toml_terminal), intent(in) :: color character(len=:), allocatable :: string string = & & repeat(" ", offset) // (color%bold + color%blue) // "-->" // color%reset // " " // source end function render_source function render_text(input, color, source) result(string) character(len=*), intent(in) :: input type(toml_terminal), intent(in) :: color character(len=*), intent(in), optional :: source character(len=:), allocatable :: string integer :: it, offset type(line_token), allocatable :: token(:) allocate(token(0)) ! avoid compiler warning token = line_tokens(input) offset = integer_width(size(token)) if (present(source)) then string = render_source(source, offset, color) // nl // & & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset else string = & & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset end if do it = 1, size(token) string = string // nl //& & render_line(input(token(it)%first:token(it)%last), to_string(it, offset), color) end do string = string // nl // & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset end function render_text function render_text_with_label(input, label, color, source) result(string) character(len=*), intent(in) :: input type(toml_label), intent(in) :: label type(toml_terminal), intent(in) :: color character(len=*), intent(in), optional :: source character(len=:), allocatable :: string integer :: it, offset, first, last, line, shift type(line_token), allocatable :: token(:) allocate(token(0)) ! avoid compiler warning token = line_tokens(input) line = count(token%first < label%first) shift = token(line)%first - 1 first = max(1, line - 1) last = min(size(token), line + 1) offset = integer_width(last) if (present(source)) then string = render_source(source, offset, color) // ":" // & & to_string(line) // ":" // & & to_string(label%first) if (label%first /= label%last) then string = string // "-" // to_string(label%last) end if end if string = string // nl // & & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset do it = first, last string = string // nl //& & render_line(input(token(it)%first:token(it)%last), & & to_string(it, offset), color) if (it == line) then string = string // nl //& & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset // & & render_label(label, shift, color) end if end do string = string // nl // & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset end function render_text_with_label pure function render_text_with_labels(input, label, color, source) result(string) character(len=*), intent(in) :: input type(toml_label), intent(in) :: label(:) type(toml_terminal), intent(in) :: color character(len=*), intent(in), optional :: source character(len=:), allocatable :: string integer :: it, il, offset, first, last, line(size(label)), shift(size(label)) type(line_token), allocatable :: token(:) logical, allocatable :: display(:) allocate(token(0)) ! avoid compiler warning allocate(character(len=0) :: string) ! Allocate to avoid referencing an unallocated variable token = line_tokens(input) line(:) = [(count(token%first <= label(it)%first), it = 1, size(label))] shift(:) = token(line)%first - 1 first = max(1, minval(line)) last = min(size(token), maxval(line)) offset = integer_width(last) it = 1 ! Without a primary we use the first label do il = 1, size(label) if (label(il)%primary) then it = il exit end if end do if (present(source)) then string = render_source(source, offset, color) // ":" // & & to_string(line(it)) // ":" // & & to_string(label(it)%first-shift(it)) if (label(it)%first /= label(it)%last) then string = string // "-" // to_string(label(it)%last-shift(it)) end if end if string = string // nl // & & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset allocate(display(first:last), source=.false.) do il = 1, size(label) ! display(max(first, line(il) - 1):min(last, line(il) + 1)) = .true. display(line(il)) = .true. end do do it = first, last if (.not.display(it)) then if (display(it-1) .and. count(display(it:)) > 0) then string = string // nl //& & repeat(" ", offset + 1) // (color%bold + color%blue) // ":" // color%reset end if cycle end if string = string // nl //& & render_line(input(token(it)%first:token(it)%last), & & to_string(it, offset), color) if (any(it == line)) then do il = 1, size(label) if (line(il) /= it) cycle string = string // nl //& & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset // & & render_label(label(il), shift(il), color) end do end if end do string = string // nl // & repeat(" ", offset + 1) // (color%bold + color%blue) // "|" // color%reset end function render_text_with_labels pure function render_label(label, shift, color) result(string) type(toml_label), intent(in) :: label integer, intent(in) :: shift type(toml_terminal), intent(in) :: color character(len=:), allocatable :: string integer :: width character :: marker type(ansi_code) :: this_color marker = merge("^", "-", label%primary) width = label%last - label%first + 1 this_color = level_color(label%level, color) string = & & repeat(" ", label%first - shift) // this_color // repeat(marker, width) // color%reset if (allocated(label%text)) then string = string // & & " " // this_color // label%text // color%reset end if end function render_label pure function level_color(level, color) result(this_color) integer, intent(in) :: level type(toml_terminal), intent(in) :: color type(ansi_code) :: this_color select case(level) case(toml_level%error) this_color = color%bold + color%red case(toml_level%warning) this_color = color%bold + color%yellow case(toml_level%help) this_color = color%bold + color%cyan case(toml_level%info) this_color = color%bold + color%magenta case default this_color = color%bold + color%blue end select end function level_color pure function render_line(input, line, color) result(string) character(len=*), intent(in) :: input character(len=*), intent(in) :: line type(toml_terminal), intent(in) :: color character(len=:), allocatable :: string string = & & line // " " // (color%bold + color%blue) // "|" // color%reset // " " // input end function render_line pure function integer_width(input) result(width) integer, intent(in) :: input integer :: width integer :: val val = input width = 0 do while (val /= 0) val = val / 10 width = width + 1 end do end function integer_width !> Represent an integer as character sequence. pure function to_string(val, width) result(string) integer, intent(in) :: val integer, intent(in), optional :: width character(len=:), allocatable :: string integer, parameter :: buffer_len = range(val)+2 character(len=buffer_len) :: buffer integer :: pos integer :: n character(len=1), parameter :: numbers(0:9) = & ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] if (val == 0) then string = numbers(0) return end if n = abs(val) buffer = "" pos = buffer_len + 1 do while (n > 0) pos = pos - 1 buffer(pos:pos) = numbers(mod(n, 10)) n = n/10 end do if (val < 0) then pos = pos - 1 buffer(pos:pos) = '-' end if if (present(width)) then string = repeat(" ", max(width-(buffer_len+1-pos), 0)) // buffer(pos:) else string = buffer(pos:) end if end function to_string end module tomlf_diagnostic fortran-toml-0.5.0/src/tomlf/meson.build0000664000175000017500000000152215201541453020376 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. subdir('build') subdir('de') subdir('structure') subdir('type') subdir('utils') srcs += files( 'all.f90', 'build.f90', 'constants.f90', 'datetime.f90', 'de.f90', 'diagnostic.f90', 'error.f90', 'ser.f90', 'structure.f90', 'terminal.f90', 'type.f90', 'utils.f90', 'version.f90', ) fortran-toml-0.5.0/src/tomlf/all.f900000664000175000017500000000153615201541453017331 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Complete reexport of the public API of TOML-Fortran module tomlf_all use tomlf_build use tomlf_constants use tomlf_datetime use tomlf_de use tomlf_error use tomlf_ser use tomlf_structure use tomlf_type use tomlf_utils use tomlf_version implicit none public end module tomlf_all fortran-toml-0.5.0/src/tomlf/structure/0000775000175000017500000000000015201541453020274 5ustar alastairalastairfortran-toml-0.5.0/src/tomlf/structure/array_list.f900000664000175000017500000001150115201541453022763 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of a basic storage structure as pointer list of pointers. !> !> This implementation does purposely not use pointer attributes in the !> datastructure to make it safer to work with. module tomlf_structure_array_list use tomlf_constants, only : tfc use tomlf_structure_list, only : toml_list_structure use tomlf_structure_node, only : toml_node, resize use tomlf_type_value, only : toml_value, toml_key implicit none private public :: toml_array_list, new_array_list !> Stores TOML values in a list of pointers type, extends(toml_list_structure) :: toml_array_list !> Current number of stored TOML values integer :: n = 0 !> List of TOML values type(toml_node), allocatable :: lst(:) contains !> Get number of TOML values in the structure procedure :: get_len !> Get TOML value at a given index procedure :: get !> Push back a TOML value to the structure procedure :: push_back !> Remove the first element from the structure procedure :: shift !> Remove the last element from the structure procedure :: pop !> Destroy the data structure procedure :: destroy end type toml_array_list !> Initial storage capacity of the datastructure integer, parameter :: initial_size = 16 contains !> Constructor for the storage data structure subroutine new_array_list(self, n) !> Instance of the structure type(toml_array_list), intent(out) :: self !> Initial storage capacity integer, intent(in), optional :: n self%n = 0 if (present(n)) then allocate(self%lst(min(1, n))) else allocate(self%lst(initial_size)) end if end subroutine new_array_list !> Get number of TOML values in the structure pure function get_len(self) result(length) !> Instance of the structure class(toml_array_list), intent(in), target :: self !> Current length of the ordered structure integer :: length length = self%n end function get_len !> Get TOML value at a given index subroutine get(self, idx, ptr) !> Instance of the structure class(toml_array_list), intent(inout), target :: self !> Position in the ordered structure integer, intent(in) :: idx !> Pointer to the stored value at given index class(toml_value), pointer, intent(out) :: ptr nullify(ptr) if (idx > 0 .and. idx <= self%n) then if (allocated(self%lst(idx)%val)) then ptr => self%lst(idx)%val end if end if end subroutine get !> Push back a TOML value to the structure subroutine push_back(self, val) !> Instance of the structure class(toml_array_list), intent(inout), target :: self !> TOML value to be stored class(toml_value), allocatable, intent(inout) :: val integer :: m if (.not.allocated(self%lst)) then call resize(self%lst, initial_size) end if m = size(self%lst) if (self%n >= m) then call resize(self%lst, m + m/2 + 1) end if self%n = self%n + 1 call move_alloc(val, self%lst(self%n)%val) end subroutine push_back !> Remove the first element from the data structure subroutine shift(self, val) !> Instance of the structure class(toml_array_list), intent(inout), target :: self !> TOML value to be retrieved class(toml_value), allocatable, intent(out) :: val integer :: i if (self%n > 0) then call move_alloc(self%lst(1)%val, val) do i = 2, self%n call move_alloc(self%lst(i)%val, self%lst(i-1)%val) end do self%n = self%n - 1 end if end subroutine shift !> Remove the last element from the data structure subroutine pop(self, val) !> Instance of the structure class(toml_array_list), intent(inout), target :: self !> TOML value to be retrieved class(toml_value), allocatable, intent(out) :: val if (self%n > 0) then call move_alloc(self%lst(self%n)%val, val) self%n = self%n - 1 end if end subroutine pop !> Deconstructor for data structure subroutine destroy(self) !> Instance of the structure class(toml_array_list), intent(inout), target :: self integer :: i do i = 1, self%n if (allocated(self%lst(i)%val)) then call self%lst(i)%val%destroy end if end do deallocate(self%lst) self%n = 0 end subroutine destroy end module tomlf_structure_array_list fortran-toml-0.5.0/src/tomlf/structure/meson.build0000664000175000017500000000120315201541453022432 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. srcs += files( 'array_list.f90', 'list.f90', 'map.f90', 'node.f90', 'ordered_map.f90', ) fortran-toml-0.5.0/src/tomlf/structure/node.f900000664000175000017500000000370215201541453021543 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of a basic storage structure as pointer list of pointers. !> !> This implementation does purposely not use pointer attributes in the !> datastructure to make it safer to work with. module tomlf_structure_node use tomlf_type_value, only : toml_value implicit none private public :: toml_node, resize !> Wrapped TOML value to generate pointer list type :: toml_node !> TOML value payload class(toml_value), allocatable :: val end type toml_node !> Initial storage capacity of the datastructure integer, parameter :: initial_size = 16 contains !> Change size of the TOML value list subroutine resize(list, n) !> Array of TOML values to be resized type(toml_node), allocatable, intent(inout), target :: list(:) !> New size of the list integer, intent(in) :: n type(toml_node), allocatable, target :: tmp(:) integer :: i if (allocated(list)) then call move_alloc(list, tmp) allocate(list(n)) do i = 1, min(size(tmp), n) if (allocated(tmp(i)%val)) then call move_alloc(tmp(i)%val, list(i)%val) end if end do do i = n+1, size(tmp) if (allocated(tmp(i)%val)) then call tmp(i)%val%destroy deallocate(tmp(i)%val) end if end do deallocate(tmp) else allocate(list(n)) end if end subroutine resize end module tomlf_structure_node fortran-toml-0.5.0/src/tomlf/structure/list.f900000664000175000017500000001001215201541453021561 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Abstract base class definitions for data structures to store TOML values module tomlf_structure_list use tomlf_constants, only : tfc use tomlf_type_value, only : toml_value, toml_key implicit none private public :: toml_list_structure !> Ordered data structure, allows iterations type, abstract :: toml_list_structure contains !> Get number of TOML values in the structure procedure(get_len), deferred :: get_len !> Push back a TOML value to the structure procedure(push_back), deferred :: push_back !> Remove the first element from the structure procedure(shift), deferred :: shift !> Remove the last element from the structure procedure(pop), deferred :: pop !> Get TOML value at a given index procedure(get), deferred :: get !> Destroy the data structure procedure(destroy), deferred :: destroy end type toml_list_structure abstract interface !> Get number of TOML values in the structure pure function get_len(self) result(length) import :: toml_list_structure !> Instance of the structure class(toml_list_structure), intent(in), target :: self !> Current length of the ordered structure integer :: length end function get_len !> Get TOML value at a given index subroutine get(self, idx, ptr) import :: toml_list_structure, toml_value !> Instance of the structure class(toml_list_structure), intent(inout), target :: self !> Position in the ordered structure integer, intent(in) :: idx !> Pointer to the stored value at given index class(toml_value), pointer, intent(out) :: ptr end subroutine get !> Push back a TOML value to the structure subroutine push_back(self, val) import :: toml_list_structure, toml_value !> Instance of the structure class(toml_list_structure), intent(inout), target :: self !> TOML value to be stored class(toml_value), allocatable, intent(inout) :: val end subroutine push_back !> Remove the first element from the data structure subroutine shift(self, val) import :: toml_list_structure, toml_value !> Instance of the structure class(toml_list_structure), intent(inout), target :: self !> TOML value to be retrieved class(toml_value), allocatable, intent(out) :: val end subroutine shift !> Remove the last element from the data structure subroutine pop(self, val) import :: toml_list_structure, toml_value !> Instance of the structure class(toml_list_structure), intent(inout), target :: self !> TOML value to be retrieved class(toml_value), allocatable, intent(out) :: val end subroutine pop !> Delete TOML value at a given key subroutine delete(self, key) import :: toml_list_structure, toml_value, tfc !> Instance of the structure class(toml_list_structure), intent(inout), target :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key end subroutine delete !> Deconstructor for data structure subroutine destroy(self) import :: toml_list_structure !> Instance of the structure class(toml_list_structure), intent(inout), target :: self end subroutine destroy end interface end module tomlf_structure_list fortran-toml-0.5.0/src/tomlf/structure/map.f900000664000175000017500000000732115201541453021374 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Abstract base class definitions for data structures to store TOML values module tomlf_structure_map use tomlf_constants, only : tfc use tomlf_type_value, only : toml_value, toml_key implicit none private public :: toml_map_structure !> Abstract data structure type, abstract :: toml_map_structure contains !> Get TOML value at a given key procedure(get), deferred :: get !> Push back a TOML value to the structure procedure(push_back), deferred :: push_back !> Get list of all keys in the structure procedure(get_keys), deferred :: get_keys !> Remove TOML value at a given key and return it procedure(pop), deferred :: pop !> Delete TOML value at a given key procedure(delete), deferred :: delete !> Destroy the data structure procedure(destroy), deferred :: destroy end type toml_map_structure abstract interface !> Get TOML value at a given key subroutine get(self, key, ptr) import :: toml_map_structure, toml_value, tfc !> Instance of the structure class(toml_map_structure), intent(inout), target :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key !> Pointer to the stored value at given key class(toml_value), pointer, intent(out) :: ptr end subroutine get !> Push back a TOML value to the structure subroutine push_back(self, val) import :: toml_map_structure, toml_value !> Instance of the structure class(toml_map_structure), intent(inout), target :: self !> TOML value to be stored class(toml_value), allocatable, intent(inout) :: val end subroutine push_back !> Get list of all keys in the structure subroutine get_keys(self, list) import :: toml_map_structure, toml_key !> Instance of the structure class(toml_map_structure), intent(inout), target :: self !> List of all keys type(toml_key), allocatable, intent(out) :: list(:) end subroutine get_keys !> Remove TOML value at a given key and return it subroutine pop(self, key, val) import :: toml_map_structure, toml_value, tfc !> Instance of the structure class(toml_map_structure), intent(inout), target :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key !> Removed TOML value class(toml_value), allocatable, intent(out) :: val end subroutine pop !> Delete TOML value at a given key subroutine delete(self, key) import :: toml_map_structure, toml_value, tfc !> Instance of the structure class(toml_map_structure), intent(inout), target :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key end subroutine delete !> Deconstructor for data structure subroutine destroy(self) import :: toml_map_structure !> Instance of the structure class(toml_map_structure), intent(inout), target :: self end subroutine destroy end interface end module tomlf_structure_map fortran-toml-0.5.0/src/tomlf/structure/CMakeLists.txt0000664000175000017500000000136015201541453023034 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set(dir "${CMAKE_CURRENT_SOURCE_DIR}") list( APPEND srcs "${dir}/array_list.f90" "${dir}/list.f90" "${dir}/map.f90" "${dir}/node.f90" "${dir}/ordered_map.f90" ) set(srcs "${srcs}" PARENT_SCOPE) fortran-toml-0.5.0/src/tomlf/structure/ordered_map.f900000664000175000017500000001270015201541453023075 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of a basic storage structure as pointer list of pointers. !> !> This implementation does purposely not use pointer attributes in the !> datastructure to make it safer to work with. module tomlf_structure_ordered_map use tomlf_constants, only : tfc use tomlf_structure_map, only : toml_map_structure use tomlf_structure_node, only : toml_node, resize use tomlf_type_value, only : toml_value, toml_key implicit none private public :: toml_ordered_map, new_ordered_map !> Stores TOML values in a list of pointers type, extends(toml_map_structure) :: toml_ordered_map !> Current number of stored TOML values integer :: n = 0 !> List of TOML values type(toml_node), allocatable :: lst(:) contains !> Get TOML value at a given key procedure :: get !> Push back a TOML value to the structure procedure :: push_back !> Remove TOML value at a given key and return it procedure :: pop !> Get list of all keys in the structure procedure :: get_keys !> Delete TOML value at a given key procedure :: delete !> Destroy the data structure procedure :: destroy end type toml_ordered_map !> Initial storage capacity of the datastructure integer, parameter :: initial_size = 16 contains !> Constructor for the storage data structure subroutine new_ordered_map(self, n) !> Instance of the structure type(toml_ordered_map), intent(out) :: self !> Initial storage capacity integer, intent(in), optional :: n self%n = 0 if (present(n)) then allocate(self%lst(min(1, n))) else allocate(self%lst(initial_size)) end if end subroutine new_ordered_map !> Get TOML value at a given key subroutine get(self, key, ptr) !> Instance of the structure class(toml_ordered_map), intent(inout), target :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key !> Pointer to the stored value at given key class(toml_value), pointer, intent(out) :: ptr integer :: i nullify(ptr) do i = 1, self%n if (allocated(self%lst(i)%val)) then if (self%lst(i)%val%match_key(key)) then ptr => self%lst(i)%val exit end if end if end do end subroutine get !> Push back a TOML value to the structure subroutine push_back(self, val) !> Instance of the structure class(toml_ordered_map), intent(inout), target :: self !> TOML value to be stored class(toml_value), allocatable, intent(inout) :: val integer :: m if (.not.allocated(self%lst)) then call resize(self%lst, initial_size) end if m = size(self%lst) if (self%n >= m) then call resize(self%lst, m + m/2 + 1) end if self%n = self%n + 1 call move_alloc(val, self%lst(self%n)%val) end subroutine push_back !> Get list of all keys in the structure subroutine get_keys(self, list) !> Instance of the structure class(toml_ordered_map), intent(inout), target :: self !> List of all keys type(toml_key), allocatable, intent(out) :: list(:) integer :: i allocate(list(self%n)) do i = 1, self%n if (allocated(self%lst(i)%val)) then if (allocated(self%lst(i)%val%key)) then list(i)%key = self%lst(i)%val%key list(i)%origin = self%lst(i)%val%origin end if end if end do end subroutine get_keys !> Remove TOML value at a given key and return it subroutine pop(self, key, val) !> Instance of the structure class(toml_ordered_map), intent(inout), target :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key !> Removed TOML value class(toml_value), allocatable, intent(out) :: val integer :: idx, i idx = 0 do i = 1, self%n if (allocated(self%lst(i)%val)) then if (self%lst(i)%val%match_key(key)) then idx = i exit end if end if end do if (idx > 0) then call move_alloc(self%lst(idx)%val, val) do i = idx+1, self%n call move_alloc(self%lst(i)%val, self%lst(i-1)%val) end do self%n = self%n - 1 end if end subroutine pop !> Delete TOML value at a given key subroutine delete(self, key) !> Instance of the structure class(toml_ordered_map), intent(inout), target :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key class(toml_value), allocatable :: val call self%pop(key, val) if (allocated(val)) then call val%destroy() end if end subroutine delete !> Deconstructor for data structure subroutine destroy(self) !> Instance of the structure class(toml_ordered_map), intent(inout), target :: self integer :: i do i = 1, self%n if (allocated(self%lst(i)%val)) then call self%lst(i)%val%destroy end if end do deallocate(self%lst) self%n = 0 end subroutine destroy end module tomlf_structure_ordered_map fortran-toml-0.5.0/src/tomlf/utils.f900000664000175000017500000001535315201541453017723 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tomlf_utils use tomlf_constants use tomlf_datetime, only : toml_datetime, toml_date, toml_time, to_string use tomlf_utils_io, only : read_whole_file, read_whole_line implicit none private public :: toml_escape_string public :: to_string public :: read_whole_file, read_whole_line interface to_string module procedure :: to_string_i1 module procedure :: to_string_i2 module procedure :: to_string_i4 module procedure :: to_string_i8 module procedure :: to_string_r8 end interface to_string contains !> Escape all special characters in a TOML string subroutine toml_escape_string(raw, escaped, multiline) !> Raw representation of TOML string character(kind=tfc, len=*), intent(in) :: raw !> Escaped view of the TOML string character(kind=tfc, len=:), allocatable, intent(out) :: escaped !> Preserve newline characters logical, intent(in), optional :: multiline integer :: i logical :: preserve_newline preserve_newline = .false. if (present(multiline)) preserve_newline = multiline escaped = '"' do i = 1, len(raw) select case(raw(i:i)) case default; escaped = escaped // raw(i:i) case('\'); escaped = escaped // '\\' case('"'); escaped = escaped // '\"' case(TOML_NEWLINE) if (preserve_newline) then escaped = escaped // raw(i:i) else escaped = escaped // '\n' end if case(TOML_FORMFEED); escaped = escaped // '\f' case(TOML_CARRIAGE_RETURN); escaped = escaped // '\r' case(TOML_TABULATOR); escaped = escaped // '\t' case(TOML_BACKSPACE); escaped = escaped // '\b' end select end do escaped = escaped // '"' end subroutine toml_escape_string !> Represent an integer as character sequence. pure function to_string_i1(val) result(string) integer, parameter :: ik = tf_i1 !> Integer value to create string from integer(ik), intent(in) :: val !> String representation of integer character(len=:), allocatable :: string integer, parameter :: buffer_len = range(val)+2 character(len=buffer_len) :: buffer integer :: pos integer(ik) :: n character(len=1), parameter :: numbers(-9:0) = & ["9", "8", "7", "6", "5", "4", "3", "2", "1", "0"] if (val == 0_ik) then string = numbers(0) return end if n = sign(val, -1_ik) buffer = "" pos = buffer_len + 1 do while (n < 0_ik) pos = pos - 1 buffer(pos:pos) = numbers(mod(n, 10_ik)) n = n/10_ik end do if (val < 0_ik) then pos = pos - 1 buffer(pos:pos) = '-' end if string = buffer(pos:) end function to_string_i1 !> Represent an integer as character sequence. pure function to_string_i2(val) result(string) integer, parameter :: ik = tf_i2 !> Integer value to create string from integer(ik), intent(in) :: val !> String representation of integer character(len=:), allocatable :: string integer, parameter :: buffer_len = range(val)+2 character(len=buffer_len) :: buffer integer :: pos integer(ik) :: n character(len=1), parameter :: numbers(-9:0) = & ["9", "8", "7", "6", "5", "4", "3", "2", "1", "0"] if (val == 0_ik) then string = numbers(0) return end if n = sign(val, -1_ik) buffer = "" pos = buffer_len + 1 do while (n < 0_ik) pos = pos - 1 buffer(pos:pos) = numbers(mod(n, 10_ik)) n = n/10_ik end do if (val < 0_ik) then pos = pos - 1 buffer(pos:pos) = '-' end if string = buffer(pos:) end function to_string_i2 !> Represent an integer as character sequence. pure function to_string_i4(val) result(string) integer, parameter :: ik = tf_i4 !> Integer value to create string from integer(ik), intent(in) :: val !> String representation of integer character(len=:), allocatable :: string integer, parameter :: buffer_len = range(val)+2 character(len=buffer_len) :: buffer integer :: pos integer(ik) :: n character(len=1), parameter :: numbers(-9:0) = & ["9", "8", "7", "6", "5", "4", "3", "2", "1", "0"] if (val == 0_ik) then string = numbers(0) return end if n = sign(val, -1_ik) buffer = "" pos = buffer_len + 1 do while (n < 0_ik) pos = pos - 1 buffer(pos:pos) = numbers(mod(n, 10_ik)) n = n/10_ik end do if (val < 0_ik) then pos = pos - 1 buffer(pos:pos) = '-' end if string = buffer(pos:) end function to_string_i4 !> Represent an integer as character sequence. pure function to_string_i8(val) result(string) integer, parameter :: ik = tf_i8 !> Integer value to create string from integer(ik), intent(in) :: val !> String representation of integer character(len=:), allocatable :: string integer, parameter :: buffer_len = range(val)+2 character(len=buffer_len) :: buffer integer :: pos integer(ik) :: n character(len=1), parameter :: numbers(-9:0) = & ["9", "8", "7", "6", "5", "4", "3", "2", "1", "0"] if (val == 0_ik) then string = numbers(0) return end if n = sign(val, -1_ik) buffer = "" pos = buffer_len + 1 do while (n < 0_ik) pos = pos - 1 buffer(pos:pos) = numbers(mod(n, 10_ik)) n = n/10_ik end do if (val < 0_ik) then pos = pos - 1 buffer(pos:pos) = '-' end if string = buffer(pos:) end function to_string_i8 !> Represent an real as character sequence. pure function to_string_r8(val) result(string) integer, parameter :: rk = tfr !> Real value to create string from real(rk), intent(in) :: val !> String representation of integer character(len=:), allocatable :: string character(128, tfc) :: buffer if (val > huge(val)) then string = "+inf" else if (val < -huge(val)) then string = "-inf" else if (val /= val) then string = "nan" else if (abs(val) >= 1.0e+100_rk) then write(buffer, '(es24.16e3)') val else if (abs(val) >= 1.0e+10_rk) then write(buffer, '(es24.16e2)') val else if (abs(val) >= 1.0e+3_rk) then write(buffer, '(es24.16e1)') val else write(buffer, '(f24.16)') val end if string = trim(adjustl(buffer)) end if end function to_string_r8 end module tomlf_utils fortran-toml-0.5.0/src/tomlf/structure.f900000664000175000017500000000467515201541453020630 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Abstraction layer for the actual storage of the data structure. !> !> The structure implementations provide the actual storage for TOML values, with !> a generic enough interface to make the definition of the TOML data structures !> independent of the actual algorithm used for storing the TOML values. !> !> Every data structure defined here should strive to only use allocatable !> data types and limit the use of pointer attributes as they interfer with !> the automatic memory management of Fortran. A well defined data structure !> in allocatables allows deep-copying of TOML values by assignment, data structures !> requiring pointer attributes have to define an assignment(=) interface to !> allow deep-copying of TOML values. module tomlf_structure use tomlf_structure_list, only : toml_list_structure use tomlf_structure_map, only : toml_map_structure use tomlf_structure_array_list, only : toml_array_list, new_array_list use tomlf_structure_ordered_map, only : toml_ordered_map, new_ordered_map implicit none private public :: toml_list_structure, toml_map_structure public :: new_list_structure, new_map_structure contains !> Constructor for the ordered storage data structure subroutine new_list_structure(self) !> Instance of the structure class(toml_list_structure), allocatable, intent(out) :: self block type(toml_array_list), allocatable :: list allocate(list) call new_array_list(list) call move_alloc(list, self) end block end subroutine new_list_structure !> Constructor for the storage data structure subroutine new_map_structure(self) !> Instance of the structure class(toml_map_structure), allocatable, intent(out) :: self block type(toml_ordered_map), allocatable :: map allocate(map) call new_ordered_map(map) call move_alloc(map, self) end block end subroutine new_map_structure end module tomlf_structure fortran-toml-0.5.0/src/tomlf/de.f900000664000175000017500000001372615201541453017155 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> TOML deserialization module !> !> This module provides interfaces for loading and parsing TOML documents !> from various sources (files, strings, and I/O units). !> !> The primary interfaces are: !> !> - [[toml_load]]: Load TOML from a file path or connected unit !> - [[toml_loads]]: Parse TOML from a string !> !> All parsing functions return an allocatable [[toml_table]] that contains !> the parsed document. If parsing fails, the table will not be allocated !> and error information is provided via the optional error argument. module tomlf_de use tomlf_constants, only : tfc, TOML_NEWLINE use tomlf_de_context, only : toml_context use tomlf_de_lexer, only : toml_lexer, new_lexer_from_string, new_lexer_from_unit, & & new_lexer_from_file use tomlf_de_parser, only : parse, toml_parser_config use tomlf_diagnostic, only : toml_level use tomlf_error, only : toml_error use tomlf_type, only : toml_table implicit none private public :: toml_parse public :: toml_load, toml_loads public :: toml_context, toml_parser_config, toml_level !> Parse a TOML document. !> !> This interface is deprecated in favor of [[toml_load]] and [[toml_loads]] interface toml_parse module procedure :: toml_parse_unit module procedure :: toml_parse_string end interface toml_parse !> Load a TOML data structure from the provided source interface toml_load module procedure :: toml_load_file module procedure :: toml_load_unit end interface toml_load !> Load a TOML data structure from a string interface toml_loads module procedure :: toml_load_string end interface toml_loads contains !> Parse a TOML input from a given IO unit. !> !> @note This procedure is deprectated subroutine toml_parse_unit(table, unit, error) !> Instance of the TOML data structure, not allocated in case of error type(toml_table), allocatable, intent(out) :: table !> Unit to read from integer, intent(in) :: unit !> Error handling, provides detailed diagnostic in case of error type(toml_error), allocatable, intent(out), optional :: error call toml_load(table, unit, error=error) end subroutine toml_parse_unit !> Wrapper to parse a TOML string. !> !> @note This procedure is deprectated subroutine toml_parse_string(table, string, error) !> Instance of the TOML data structure, not allocated in case of error type(toml_table), allocatable, intent(out) :: table !> String containing TOML document character(len=*), intent(in), target :: string !> Error handling, provides detailed diagnostic in case of error type(toml_error), allocatable, intent(out), optional :: error call toml_loads(table, string, error=error) end subroutine toml_parse_string !> Load TOML data structure from file subroutine toml_load_file(table, filename, config, context, error) !> Instance of the TOML data structure, not allocated in case of error type(toml_table), allocatable, intent(out) :: table character(*, tfc), intent(in) :: filename !> Configuration for the parser type(toml_parser_config), intent(in), optional :: config !> Context tracking the origin of the data structure to allow rich reports type(toml_context), intent(out), optional :: context !> Error handling, provides detailed diagnostic in case of error type(toml_error), allocatable, intent(out), optional :: error type(toml_lexer) :: lexer type(toml_error), allocatable :: error_ call new_lexer_from_file(lexer, filename, error_) if (.not.allocated(error_)) then call parse(lexer, table, config, context, error) else if (present(error)) call move_alloc(error_, error) end if end subroutine toml_load_file !> Load TOML data structure from unit subroutine toml_load_unit(table, io, config, context, error) !> Instance of the TOML data structure, not allocated in case of error type(toml_table), allocatable, intent(out) :: table !> Unit to read from integer, intent(in) :: io !> Configuration for the parser type(toml_parser_config), intent(in), optional :: config !> Context tracking the origin of the data structure to allow rich reports type(toml_context), intent(out), optional :: context !> Error handling, provides detailed diagnostic in case of error type(toml_error), allocatable, intent(out), optional :: error type(toml_lexer) :: lexer type(toml_error), allocatable :: error_ call new_lexer_from_unit(lexer, io, error_) if (.not.allocated(error_)) then call parse(lexer, table, config, context, error) else if (present(error)) call move_alloc(error_, error) end if end subroutine toml_load_unit !> Load TOML data structure from string subroutine toml_load_string(table, string, config, context, error) !> Instance of the TOML data structure, not allocated in case of error type(toml_table), allocatable, intent(out) :: table !> String containing TOML document character(*, tfc), intent(in) :: string !> Configuration for the parser type(toml_parser_config), intent(in), optional :: config !> Context tracking the origin of the data structure to allow rich reports type(toml_context), intent(out), optional :: context !> Error handling, provides detailed diagnostic in case of error type(toml_error), allocatable, intent(out), optional :: error type(toml_lexer) :: lexer call new_lexer_from_string(lexer, string) call parse(lexer, table, config, context, error) end subroutine toml_load_string end module tomlf_de fortran-toml-0.5.0/src/tomlf/de/0000775000175000017500000000000015201541453016624 5ustar alastairalastairfortran-toml-0.5.0/src/tomlf/de/meson.build0000664000175000017500000000117515201541453020772 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. srcs += files( 'abc.f90', 'context.f90', 'lexer.f90', 'parser.f90', 'token.f90', ) fortran-toml-0.5.0/src/tomlf/de/parser.f900000664000175000017500000006741615201541453020456 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of a parser for transforming a token stream to TOML datastructures. module tomlf_de_parser use tomlf_constants, only : tfc, tfr, tfi, TOML_NEWLINE use tomlf_datetime, only : toml_datetime use tomlf_de_context, only : toml_context use tomlf_de_abc, only : toml_lexer => abstract_lexer use tomlf_de_token, only : toml_token, token_kind, stringify use tomlf_diagnostic, only : render, toml_diagnostic, toml_label, toml_level use tomlf_terminal, only : toml_terminal use tomlf_error, only : toml_error, toml_stat use tomlf_type, only : toml_table, toml_array, toml_keyval, toml_value, toml_key, & & add_table, add_array, add_keyval, cast_to_table, cast_to_array, len implicit none private public :: toml_parser, toml_parser_config, parse !> Configuration of the TOML parser type :: toml_parser_config !> Use colorful output for diagnostics type(toml_terminal) :: color = toml_terminal() !> Record all tokens integer :: context_detail = 0 end type toml_parser_config interface toml_parser_config module procedure :: new_parser_config end interface toml_parser_config !> TOML parser type :: toml_parser !> Current token type(toml_token) :: token !> Table containing the document root type(toml_table), allocatable :: root !> Pointer to the currently processed table type(toml_table), pointer :: current !> Diagnostic produced while parsing type(toml_diagnostic), allocatable :: diagnostic !> Context for producing diagnostics type(toml_context) :: context !> Configuration of the parser type(toml_parser_config) :: config end type toml_parser contains !> Create a new instance of the TOML parser subroutine new_parser(parser, config) !> Instance of the parser type(toml_parser), intent(out), target :: parser !> Configuration of the parser type(toml_parser_config), intent(in), optional :: config parser%token = toml_token(token_kind%newline, 0, 0) parser%root = toml_table() parser%current => parser%root parser%config = toml_parser_config() if (present(config)) parser%config = config end subroutine new_parser !> Create new configuration for the TOML parser pure function new_parser_config(color, context_detail) result(config) !> Configuration of the parser type(toml_parser_config) :: config !> Color support for diagnostics logical, intent(in), optional :: color !> Record all tokens integer, intent(in), optional :: context_detail if (present(color)) config%color = toml_terminal(color) if (present(context_detail)) config%context_detail = context_detail end function new_parser_config !> Parse TOML document and return root table subroutine parse(lexer, table, config, context, error) !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> TOML data structure type(toml_table), allocatable, intent(out) :: table !> Configuration for the parser type(toml_parser_config), intent(in), optional :: config !> Context tracking the origin of the data structure to allow rich reports type(toml_context), intent(out), optional :: context !> Error handler type(toml_error), allocatable, intent(out), optional :: error type(toml_parser) :: parser call new_parser(parser, config) call parse_root(parser, lexer) if (present(error) .and. allocated(parser%diagnostic)) then call make_error(error, parser%diagnostic, lexer, parser%config%color) end if if (allocated(parser%diagnostic)) return call move_alloc(parser%root, table) if (present(context)) then context = parser%context call lexer%get_info("filename", context%filename) call lexer%get_info("source", context%source) end if end subroutine parse !> Parse the root table subroutine parse_root(parser, lexer) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer do while(.not.allocated(parser%diagnostic) .and. parser%token%kind /= token_kind%eof) select case(parser%token%kind) case(token_kind%newline, token_kind%whitespace, token_kind%comment) call next_token(parser, lexer) case(token_kind%keypath, token_kind%string, token_kind%literal) call parse_keyval(parser, lexer, parser%current) case(token_kind%lbracket) call parse_table_header(parser, lexer) case default call syntax_error(parser%diagnostic, lexer, parser%token, & & "Invalid syntax", & & "unexpected "//stringify(parser%token)) end select end do end subroutine parse_root !> Parse a table or array of tables header subroutine parse_table_header(parser, lexer) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer type(toml_array), pointer :: array type(toml_table), pointer :: table class(toml_value), pointer :: ptr type(toml_key) :: key logical :: array_of_tables integer, parameter :: initial_size = 8 integer :: top type(toml_key), allocatable :: stack(:) type(toml_token), allocatable :: leading_whitespace, trailing_whitespace call consume(parser, lexer, token_kind%lbracket) if (allocated(parser%diagnostic)) return if (parser%token%kind == token_kind%whitespace) then leading_whitespace = parser%token call next_token(parser, lexer) end if array_of_tables = parser%token%kind == token_kind%lbracket if (array_of_tables) then call next_token(parser, lexer) if (parser%token%kind == token_kind%whitespace) then call next_token(parser, lexer) end if end if call fill_stack(lexer, parser, top, stack) if (allocated(parser%diagnostic)) return key = stack(top) top = top - 1 call walk_stack(parser, top, stack) if (array_of_tables) then call parser%current%get(key%key, ptr) if (associated(ptr)) then array => cast_to_array(ptr) if (.not.associated(array)) then call duplicate_key_error(parser%diagnostic, lexer, & & parser%context%token(key%origin), & & parser%context%token(ptr%origin), & & "Key '"//key%key//"' already exists") return end if if (array%inline) then call semantic_error(parser%diagnostic, lexer, & & parser%context%token(key%origin), & & parser%context%token(array%origin), & & "Array of tables cannot extend inline array", & & "extended here", & & "defined as inline") return end if else call add_array(parser%current, key, array) array%inline = .false. end if call add_table(array, table) else call parser%current%get(key%key, ptr) if (associated(ptr)) then table => cast_to_table(ptr) if (associated(table)) then if (.not.table%implicit) nullify(table) end if if (.not.associated(table)) then call duplicate_key_error(parser%diagnostic, lexer, & & parser%context%token(key%origin), & & parser%context%token(ptr%origin), & & "Key '"//key%key//"' already exists") return end if else call add_table(parser%current, key, table) end if end if parser%current => table call consume(parser, lexer, token_kind%rbracket) if (allocated(parser%diagnostic)) return if (array_of_tables) then if (parser%token%kind == token_kind%whitespace) then trailing_whitespace = parser%token call next_token(parser, lexer) end if call consume(parser, lexer, token_kind%rbracket) if (allocated(parser%diagnostic)) return end if if (array_of_tables .and. allocated(leading_whitespace)) then call syntax_error(parser%diagnostic, lexer, leading_whitespace, & & "Malformatted array of table header encountered", & & "whitespace not allowed in header") return end if if (array_of_tables .and. allocated(trailing_whitespace)) then call syntax_error(parser%diagnostic, lexer, trailing_whitespace, & & "Malformatted array of table header encountered", & & "whitespace not allowed in header") return end if do while(parser%token%kind == token_kind%whitespace) call next_token(parser, lexer) end do if (parser%token%kind == token_kind%comment) then call next_token(parser, lexer) end if if (all(parser%token%kind /= [token_kind%newline, token_kind%eof])) then call syntax_error(parser%diagnostic, lexer, parser%token, & & "Unexpected "//stringify(parser%token)//" after table header", & & "expected newline") end if contains !> Fill the stack with tokens subroutine fill_stack(lexer, parser, top, stack) class(toml_lexer), intent(inout) :: lexer type(toml_parser), intent(inout) :: parser !> Depth of the table key stack integer, intent(out) :: top !> Stack of all keys in the table header type(toml_key), allocatable, intent(out) :: stack(:) top = 0 allocate(stack(initial_size)) do if (top >= size(stack)) then call resize(stack) end if if (all(parser%token%kind /= [token_kind%string, token_kind%literal, & & token_kind%keypath])) then call syntax_error(parser%diagnostic, lexer, parser%token, & & "Missing key for table header", & & "unexpected "//stringify(parser%token)) return end if top = top + 1 call extract_key(parser, lexer, stack(top)) call next_token(parser, lexer) if (parser%token%kind == token_kind%whitespace) & & call next_token(parser, lexer) if (parser%token%kind == token_kind%rbracket) exit call consume(parser, lexer, token_kind%dot) if (allocated(parser%diagnostic)) return if (parser%token%kind == token_kind%whitespace) & & call next_token(parser, lexer) end do if (top <= 0) then call syntax_error(parser%diagnostic, lexer, parser%token, & & "Empty table header", & & "expected table header") end if end subroutine fill_stack !> Walk the key stack to fetch the correct table, create implicit tables as necessary subroutine walk_stack(parser, top, stack) type(toml_parser), intent(inout), target :: parser !> Depth of the table key stack integer, intent(in) :: top !> Stack of all keys in the table header type(toml_key), intent(in), target :: stack(:) type(toml_table), pointer :: table, tmp_tbl type(toml_array), pointer :: array type(toml_key), pointer :: key class(toml_value), pointer :: ptr integer :: it table => parser%root do it = 1, top key => stack(it) if (.not.table%has_key(key%key)) then call add_table(table, key, tmp_tbl) if (associated(tmp_tbl)) then tmp_tbl%implicit = .true. end if end if call table%get(key%key, ptr) table => cast_to_table(ptr) if (.not.associated(table)) then array => cast_to_array(ptr) if (associated(array)) then call array%get(len(array), ptr) table => cast_to_table(ptr) end if if (.not.associated(table)) then call duplicate_key_error(parser%diagnostic, lexer, & & parser%context%token(key%origin), & & parser%context%token(ptr%origin), & & "Key '"//key%key//"' already exists") return end if end if if (table%inline) then call semantic_error(parser%diagnostic, lexer, & & parser%context%token(key%origin), & & parser%context%token(table%origin), & & "Inline table '"//key%key//"' cannot be used as a key", & & "inline table cannot be extended", & & "defined as inline first") end if end do parser%current => table end subroutine walk_stack !> Change size of the stack subroutine resize(stack, n) !> Stack of keys to be resized type(toml_key), allocatable, intent(inout) :: stack(:) !> New size of the stack integer, intent(in), optional :: n type(toml_key), allocatable :: tmp(:) integer :: m if (present(n)) then m = n else if (allocated(stack)) then m = size(stack) m = m + m/2 + 1 else m = initial_size end if end if if (allocated(stack)) then call move_alloc(stack, tmp) allocate(stack(m)) m = min(size(tmp), m) stack(:m) = tmp(:m) deallocate(tmp) else allocate(stack(m)) end if end subroutine resize end subroutine parse_table_header !> Parse key value pairs in a table body recursive subroutine parse_keyval(parser, lexer, table) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Current table type(toml_table), intent(inout) :: table class(toml_value), pointer :: ptr type(toml_keyval), pointer :: vptr type(toml_array), pointer :: aptr type(toml_table), pointer :: tptr type(toml_key) :: key call extract_key(parser, lexer, key) call next_token(parser, lexer) if (parser%token%kind == token_kind%whitespace) & call next_token(parser, lexer) if (parser%token%kind == token_kind%dot) then call get_table(table, key, tptr) if (tptr%inline) then call semantic_error(parser%diagnostic, lexer, & & parser%context%token(key%origin), & & parser%context%token(tptr%origin), & & "Cannot add keys to inline tables", & & "inline table cannot be extended", & & "defined as inline first") return end if call next_token(parser, lexer) if (parser%token%kind == token_kind%whitespace) & call next_token(parser, lexer) if (any(parser%token%kind == [token_kind%keypath, token_kind%string, & & token_kind%literal])) then call parse_keyval(parser, lexer, tptr) else call syntax_error(parser%diagnostic, lexer, parser%token, & & "Invalid syntax", & & "expected key") end if return end if call consume(parser, lexer, token_kind%equal) if (allocated(parser%diagnostic)) return if (parser%token%kind == token_kind%whitespace) & call next_token(parser, lexer) call table%get(key%key, ptr) if (associated(ptr)) then call duplicate_key_error(parser%diagnostic, lexer, & & parser%context%token(key%origin), & & parser%context%token(ptr%origin), & & "Key '"//key%key//"' already exists") return end if select case(parser%token%kind) case default call add_keyval(table, key, vptr) call parse_value(parser, lexer, vptr) case(token_kind%nil) call next_token(parser, lexer) case(token_kind%lbracket) call add_array(table, key, aptr) call parse_inline_array(parser, lexer, aptr) case(token_kind%lbrace) call add_table(table, key, tptr) call parse_inline_table(parser, lexer, tptr) end select if (allocated(parser%diagnostic)) return if (parser%token%kind == token_kind%whitespace) & call next_token(parser, lexer) if (parser%token%kind == token_kind%comment) & call next_token(parser, lexer) end subroutine parse_keyval recursive subroutine parse_inline_array(parser, lexer, array) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Current array type(toml_array), intent(inout) :: array type(toml_keyval), pointer :: vptr type(toml_array), pointer :: aptr type(toml_table), pointer :: tptr integer, parameter :: skip_tokens(*) = & [token_kind%whitespace, token_kind%comment, token_kind%newline] array%inline = .true. call consume(parser, lexer, token_kind%lbracket) inline_array: do while(.not.allocated(parser%diagnostic)) do while(any(parser%token%kind == skip_tokens)) call next_token(parser, lexer) end do select case(parser%token%kind) case(token_kind%rbracket) exit inline_array case default call add_keyval(array, vptr) call parse_value(parser, lexer, vptr) case(token_kind%nil) call next_token(parser, lexer) case(token_kind%lbracket) call add_array(array, aptr) call parse_inline_array(parser, lexer, aptr) case(token_kind%lbrace) call add_table(array, tptr) call parse_inline_table(parser, lexer, tptr) end select if (allocated(parser%diagnostic)) exit inline_array do while(any(parser%token%kind == skip_tokens)) call next_token(parser, lexer) end do if (parser%token%kind == token_kind%comma) then call next_token(parser, lexer) cycle inline_array end if exit inline_array end do inline_array if (allocated(parser%diagnostic)) return call consume(parser, lexer, token_kind%rbracket) end subroutine parse_inline_array recursive subroutine parse_inline_table(parser, lexer, table) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Current table type(toml_table), intent(inout) :: table integer, parameter :: skip_tokens(*) = & [token_kind%whitespace, token_kind%comment, token_kind%newline] table%inline = .true. call consume(parser, lexer, token_kind%lbrace) inline_table: do while(.not.allocated(parser%diagnostic)) do while(any(parser%token%kind == skip_tokens)) call next_token(parser, lexer) end do select case(parser%token%kind) case(token_kind%rbrace) exit inline_table case default call syntax_error(parser%diagnostic, lexer, parser%token, & & "Invalid character in inline table", & & "unexpected "//stringify(parser%token)) case(token_kind%keypath, token_kind%string, token_kind%literal) call parse_keyval(parser, lexer, table) end select if (allocated(parser%diagnostic)) exit inline_table do while(any(parser%token%kind == skip_tokens)) call next_token(parser, lexer) end do if (parser%token%kind == token_kind%comma) then call next_token(parser, lexer) cycle inline_table end if exit inline_table end do inline_table if (allocated(parser%diagnostic)) return call consume(parser, lexer, token_kind%rbrace) end subroutine parse_inline_table subroutine parse_value(parser, lexer, kval) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Current key value pair type(toml_keyval), intent(inout) :: kval select case(parser%token%kind) case default call syntax_error(parser%diagnostic, lexer, parser%token, & & "Invalid expression for value", & & "unexpected "//stringify(parser%token)) case(token_kind%unclosed) ! Handle runaway expressions separately call syntax_error(parser%diagnostic, lexer, parser%token, & & "Inline expression contains unclosed or runaway group", & & "unclosed inline expression") case(token_kind%string, token_kind%mstring, token_kind%literal, token_kind%mliteral, & & token_kind%int, token_kind%float, token_kind%bool, token_kind%datetime) call extract_value(parser, lexer, kval) call next_token(parser, lexer) end select end subroutine parse_value !> Check whether the current token is the expected one and advance the lexer subroutine consume(parser, lexer, kind) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Expected token kind integer, intent(in) :: kind if (parser%token%kind /= kind) then call syntax_error(parser%diagnostic, lexer, parser%token, & & "Invalid syntax in this context", & & "expected "//stringify(toml_token(kind))) return end if call next_token(parser, lexer) end subroutine consume !> Create diagnostic for invalid syntax subroutine syntax_error(diagnostic, lexer, token, message, label) !> Diagnostic for the syntax error type(toml_diagnostic), allocatable, intent(out) :: diagnostic !> Instance of the lexer providing the context class(toml_lexer), intent(inout) :: lexer !> Token that caused the error type(toml_token), intent(in) :: token !> Message for the error character(len=*), intent(in) :: message !> Label for the token character(len=*), intent(in) :: label character(:, tfc), allocatable :: filename call lexer%get_info("filename", filename) allocate(diagnostic) diagnostic = toml_diagnostic( & & toml_level%error, & & message, & & filename, & & [toml_label(toml_level%error, token%first, token%last, label, .true.)]) end subroutine syntax_error !> Create diagnostic for incorrect semantics subroutine semantic_error(diagnostic, lexer, token1, token2, message, label1, label2) !> Diagnostic for the duplicate key error type(toml_diagnostic), allocatable, intent(out) :: diagnostic !> Instance of the lexer providing the context class(toml_lexer), intent(inout) :: lexer !> Token identifying the duplicate key type(toml_token), intent(in) :: token1 !> Token identifying the original key type(toml_token), intent(in) :: token2 !> Message for the error character(len=*), intent(in) :: message !> Label for the first token character(len=*), intent(in) :: label1 !> Label for the second token character(len=*), intent(in) :: label2 character(:, tfc), allocatable :: filename call lexer%get_info("filename", filename) allocate(diagnostic) diagnostic = toml_diagnostic( & & toml_level%error, & & message, & & filename, & & [toml_label(toml_level%error, token1%first, token1%last, label1, .true.), & & toml_label(toml_level%info, token2%first, token2%last, label2, .false.)]) end subroutine semantic_error !> Create a diagnostic for a duplicate key entry subroutine duplicate_key_error(diagnostic, lexer, token1, token2, message) !> Diagnostic for the duplicate key error type(toml_diagnostic), allocatable, intent(out) :: diagnostic !> Instance of the lexer providing the context class(toml_lexer), intent(inout) :: lexer !> Token identifying the duplicate key type(toml_token), intent(in) :: token1 !> Token identifying the original key type(toml_token), intent(in) :: token2 !> Message for the error character(len=*), intent(in) :: message call semantic_error(diagnostic, lexer, token1, token2, & & message, "key already used", "first defined here") end subroutine duplicate_key_error !> Create an error from a diagnostic subroutine make_error(error, diagnostic, lexer, color) !> Error to be created type(toml_error), allocatable, intent(out) :: error !> Diagnostic to be used type(toml_diagnostic), intent(in) :: diagnostic !> Instance of the lexer providing the context class(toml_lexer), intent(in) :: lexer !> Use colorful error messages type(toml_terminal), intent(in) :: color character(len=:), allocatable :: str allocate(error) call lexer%get_info("source", str) error%message = render(diagnostic, str, color) error%stat = toml_stat%fatal end subroutine make_error !> Wrapper around the lexer to retrieve the next token. !> Allows to record the tokens for keys and values in the parser context subroutine next_token(parser, lexer) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer call lexer%next(parser%token) select case(parser%token%kind) case(token_kind%keypath, token_kind%string, token_kind%literal, token_kind%int, & & token_kind%float, token_kind%bool, token_kind%datetime) call parser%context%push_back(parser%token) case(token_kind%newline, token_kind%dot, token_kind%comma, token_kind%equal, & & token_kind%lbrace, token_kind%rbrace, token_kind%lbracket, token_kind%rbracket) if (parser%config%context_detail > 0) & call parser%context%push_back(parser%token) case default if (parser%config%context_detail > 1) & call parser%context%push_back(parser%token) end select end subroutine next_token !> Extract key from token subroutine extract_key(parser, lexer, key) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Key to be extracted type(toml_key), intent(out) :: key call lexer%extract(parser%token, key%key) key%origin = parser%context%top if (scan(key%key, TOML_NEWLINE) > 0) then call syntax_error(parser%diagnostic, lexer, parser%token, & & "Invalid character in key", & & "key cannot contain newline") return end if end subroutine extract_key !> Extract value from token subroutine extract_value(parser, lexer, kval) !> Instance of the parser class(toml_parser), intent(inout) :: parser !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Value to be extracted type(toml_keyval), intent(inout) :: kval character(:, tfc), allocatable :: sval real(tfr) :: rval integer(tfi) :: ival logical :: bval type(toml_datetime) :: dval kval%origin_value = parser%context%top select case(parser%token%kind) case(token_kind%string, token_kind%literal, token_kind%mstring, token_kind%mliteral) call lexer%extract_string(parser%token, sval) call kval%set(sval) case(token_kind%int) call lexer%extract_integer(parser%token, ival) call kval%set(ival) case(token_kind%float) call lexer%extract_float(parser%token, rval) call kval%set(rval) case(token_kind%bool) call lexer%extract_bool(parser%token, bval) call kval%set(bval) case(token_kind%datetime) call lexer%extract_datetime(parser%token, dval) call kval%set(dval) end select end subroutine extract_value !> Try to retrieve TOML table with key or create it subroutine get_table(table, key, ptr, stat) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key for the new table type(toml_key), intent(in) :: key !> Pointer to the newly created table type(toml_table), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat class(toml_value), pointer :: tmp nullify(ptr) call table%get(key%key, tmp) if (associated(tmp)) then ptr => cast_to_table(tmp) if (present(stat)) stat = merge(toml_stat%success, toml_stat%fatal, associated(ptr)) else call add_table(table, key, ptr, stat) end if end subroutine get_table end module tomlf_de_parser fortran-toml-0.5.0/src/tomlf/de/context.f900000664000175000017500000001154015201541453020631 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Provides a container to store tokens for later use module tomlf_de_context use tomlf_constants, only : tfc use tomlf_de_token, only : toml_token, resize use tomlf_diagnostic, only : toml_diagnostic, toml_label, render, toml_level use tomlf_terminal, only : toml_terminal implicit none private public :: toml_context !> Container storing tokens type :: toml_context !> Filename of the input character(:, tfc), allocatable :: filename !> Actual source character(:, tfc), allocatable :: source !> Stack of stored tokens type(toml_token), allocatable :: token(:) !> Last stored token integer :: top = 0 contains !> Push a new token to the stack procedure :: push_back !> Create a report generic :: report => report1, report2 !> Create a report with a single label procedure :: report1 !> Create a report with a two labels procedure :: report2 end type toml_context contains !> Push a new token to the stack subroutine push_back(self, token) !> Instance of the token storage class(toml_context), intent(inout) :: self !> New token to be added type(toml_token), intent(in) :: token if (.not.allocated(self%token)) call resize(self%token) if (self%top >= size(self%token)) call resize(self%token) self%top = self%top + 1 self%token(self%top) = token end subroutine push_back !> Create a report with a single label pure function report1(self, message, origin, label, level, color) result(string) !> Instance of the token storage class(toml_context), intent(in) :: self !> Message for the report character(*, tfc), intent(in) :: message !> Position to report at integer, intent(in) :: origin !> String for the label character(*, tfc), intent(in), optional :: label !> Highlight level integer, intent(in), optional :: level !> Color terminal type(toml_terminal), intent(in), optional :: color !> Final rendered report character(:, tfc), allocatable :: string type(toml_diagnostic) :: diagnostic type(toml_label), allocatable :: labels(:) integer :: level_ level_ = toml_level%error if (present(level)) level_ = level if (origin > 0 .and. origin <= self%top) then allocate(labels(1)) labels(1) = toml_label(level_, & & self%token(origin)%first, self%token(origin)%last, label, .true.) end if diagnostic = toml_diagnostic( & & level_, & & message, & & self%filename, & & labels) if (.not.present(color)) then string = render(diagnostic, self%source, toml_terminal(.false.)) else string = render(diagnostic, self%source, color) end if end function report1 !> Create a report with two labels pure function report2(self, message, origin1, origin2, label1, label2, level1, level2, color) & & result(string) !> Instance of the token storage class(toml_context), intent(in) :: self !> Message for the report character(*, tfc), intent(in) :: message !> Position to report at integer, intent(in) :: origin1, origin2 !> String for the label character(*, tfc), intent(in), optional :: label1, label2 !> Highlight level integer, intent(in), optional :: level1, level2 !> Color terminal type(toml_terminal), intent(in), optional :: color !> Final rendered report character(:, tfc), allocatable :: string type(toml_diagnostic) :: diagnostic type(toml_label), allocatable :: labels(:) integer :: level1_, level2_ level1_ = toml_level%error if (present(level1)) level1_ = level1 level2_ = toml_level%info if (present(level2)) level2_ = level2 if (origin1 > 0 .and. origin1 <= self%top & & .and. origin2 > 0 .and. origin2 <= self%top) then allocate(labels(2)) labels(1) = toml_label(level1_, & & self%token(origin1)%first, self%token(origin1)%last, label1, .true.) labels(2) = toml_label(level2_, & & self%token(origin2)%first, self%token(origin2)%last, label2, .false.) end if diagnostic = toml_diagnostic( & & level1_, & & message, & & self%filename, & & labels) if (.not.present(color)) then string = render(diagnostic, self%source, toml_terminal(.false.)) else string = render(diagnostic, self%source, color) end if end function report2 end module tomlf_de_context fortran-toml-0.5.0/src/tomlf/de/lexer.f900000664000175000017500000013760115201541453020273 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Provides tokenization for TOML documents. !> !> The lexer provides a way to turn a stream of characters into tokens which !> are further processed by the parser and turned into actual TOML data structures. !> In the current structure no knowledge about the character stream is required !> in the parser to generate the data structures. !> !> The validity of all tokens can be guaranteed by the lexer, however syntax errors !> and semantic errors are not detected until the parser is run. Identification of !> invalid tokens and recovery of the tokenization is done on a best effort basis. !> !> To avoid overflows in the parser due to deeply nested but unclosed groups, the !> lexer will always tokenize a complete group to verify it is closed properly. !> Unclosed groups will lead to the first token of the group getting invalidated, !> to allow reporting in the parsing phase. module tomlf_de_lexer use tomlf_constants, only : tfc, tfi, tfr, TOML_BACKSPACE, TOML_TABULATOR, TOML_NEWLINE, & & TOML_CARRIAGE_RETURN, TOML_FORMFEED, TOML_ESC use tomlf_datetime, only : toml_datetime, toml_date, toml_time use tomlf_de_abc, only : abstract_lexer use tomlf_de_context, only : toml_context use tomlf_de_token, only : toml_token, stringify, token_kind, resize use tomlf_error, only : toml_error, toml_stat, make_error use tomlf_utils, only : read_whole_file, read_whole_line implicit none private public :: toml_lexer, new_lexer_from_file, new_lexer_from_unit, new_lexer_from_string public :: toml_token, stringify, token_kind !> Possible characters encountered in a lexeme type :: enum_char character(1, tfc) :: space = tfc_" " character(1, tfc) :: hash = tfc_"#" character(1, tfc) :: squote = tfc_"'" character(3, tfc) :: squote3 = repeat(tfc_"'", 3) character(1, tfc) :: dquote = tfc_"""" character(3, tfc) :: dquote3 = repeat(tfc_"""", 3) character(1, tfc) :: backslash = tfc_"\" character(1, tfc) :: dot = tfc_"." character(1, tfc) :: comma = tfc_"," character(1, tfc) :: equal = tfc_"=" character(1, tfc) :: lbrace = tfc_"{" character(1, tfc) :: rbrace = tfc_"}" character(1, tfc) :: lbracket = tfc_"[" character(1, tfc) :: rbracket = tfc_"]" character(1, tfc) :: newline = achar(10, kind=tfc) character(1, tfc) :: formfeed = achar(12, kind=tfc) character(1, tfc) :: carriage_return = achar(13, kind=tfc) character(1, tfc) :: bspace = achar(8, kind=tfc) character(1, tfc) :: tab = achar(9, kind=tfc) character(1, tfc) :: plus = tfc_"+" character(1, tfc) :: minus = tfc_"-" character(12, tfc) :: literal = tfc_"0123456789-_" end type enum_char !> Actual enumerator for possible characters type(enum_char), parameter :: char_kind = enum_char() !> Set of characters marking a terminated lexeme, mainly used for values and to !> obtain boundaries of invalid tokens. character(*, tfc), parameter :: terminated = & & char_kind%space//char_kind%tab//char_kind%newline//char_kind%carriage_return//& & char_kind%hash//char_kind%rbrace//char_kind%rbracket//char_kind%comma//& & char_kind%equal !> Scopes to identify the state of the lexer. type :: enum_scope !> Table scopes allow keypaths, in this scenario only bare keys, strings and !> literals are allowed, furthermore dots become special characters to separate !> the keypaths. integer :: table = 1 !> Terminates a table scope and opens a value scope. Here usual values, like integer, !> floats or strings are allowed. integer :: equal = 2 !> Opens an array scope, similar to the value scope for allowed characters but with !> simplified closing rules to allow handling of values and inline tables in arrays. integer :: array = 3 end type enum_scope !> Actual enumerator for auxiliary scopes type(enum_scope), parameter :: lexer_scope = enum_scope() !> Item identifying the scope and the corresponding token index type :: stack_item !> Current scope of the item, can only be removed with matching scope integer :: scope !> Token index in the buffer of the lexer, used for invalidation of unclosed groups integer :: token end type stack_item !> Reallocate the stack of scopes interface resize module procedure :: resize_scope end interface !> Tokenizer for TOML documents. type, extends(abstract_lexer) :: toml_lexer !> Name of the source file, used for error reporting character(len=:), allocatable :: filename !> Current internal position in the source chunk integer :: pos = 0 !> Current source chunk, for convenience stored as character array rather than string character(:, tfc), allocatable :: chunk !> Last scope of the lexer integer :: top = 0 !> Stack of scopes, used to identify the current state of the lexer type(stack_item), allocatable :: stack(:) !> Index in the buffer queue integer :: buffer = 0 !> Douple-ended queue for buffering tokens type(toml_context) :: context contains !> Obtain the next token procedure :: next !> Extract a string from a token procedure :: extract_string !> Extract an integer from a token procedure :: extract_integer !> Extract a float from a token procedure :: extract_float !> Extract a boolean from a token procedure :: extract_bool !> Extract a timestamp from a token procedure :: extract_datetime !> Get information about source procedure :: get_info end type toml_lexer contains !> Create a new instance of a lexer by reading from a file subroutine new_lexer_from_file(lexer, filename, error) !> Instance of the lexer type(toml_lexer), intent(out) :: lexer !> Name of the file to read from character(len=*), intent(in) :: filename !> Error code type(toml_error), allocatable, intent(out) :: error integer :: stat lexer%pos = 0 lexer%filename = filename call resize(lexer%stack) call read_whole_file(filename, lexer%chunk, stat) if (stat /= 0) then call make_error(error, "Could not open file '"//filename//"'") end if end subroutine new_lexer_from_file !> Create a new instance of a lexer by reading from a unit. !> !> Currently, only sequential access units can be processed by this constructor. subroutine new_lexer_from_unit(lexer, io, error) !> Instance of the lexer type(toml_lexer), intent(out) :: lexer !> Unit to read from integer, intent(in) :: io !> Error code type(toml_error), allocatable, intent(out) :: error character(:, tfc), allocatable :: source, line integer, parameter :: bufsize = 512 character(bufsize, tfc) :: filename, mode integer :: stat inquire(unit=io, access=mode, name=filename) select case(trim(mode)) case default stat = 1 case("sequential", "SEQUENTIAL") allocate(character(0) :: source) do call read_whole_line(io, line, stat) if (stat > 0) exit source = source // line // TOML_NEWLINE if (stat < 0) then if (is_iostat_end(stat)) stat = 0 exit end if end do call new_lexer_from_string(lexer, source) end select if (len_trim(filename) > 0) lexer%filename = trim(filename) if (stat /= 0) then call make_error(error, "Failed to read from unit") end if end subroutine new_lexer_from_unit !> Create a new instance of a lexer by reading from a string. subroutine new_lexer_from_string(lexer, string) !> Instance of the lexer type(toml_lexer), intent(out) :: lexer !> String to read from character(*, tfc), intent(in) :: string integer :: length length = len(string) lexer%pos = 0 lexer%buffer = 0 allocate(character(length) :: lexer%chunk) lexer%chunk(:length) = string call resize(lexer%stack) end subroutine new_lexer_from_string !> Advance the lexer to the next token. subroutine next(lexer, token) !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token if (lexer%buffer >= lexer%context%top) then call fill_buffer(lexer) end if lexer%buffer = lexer%buffer + 1 token = lexer%context%token(lexer%buffer) end subroutine next !> Fill the buffer with tokens, this routine will attempt to create as many tokens as !> necessary to determine whether all opened groups are closed properly. !> !> The state of the buffer can be changed while this routine is running, therefore !> accessing the buffer concurrently is not allowed. subroutine fill_buffer(lexer) !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer type(toml_token) :: token integer :: stack_top, it lexer%buffer = 0 lexer%context%top = 0 stack_top = lexer%top ! Tokenization will cover always a complete scope do while(lexer%top >= stack_top .and. token%kind /= token_kind%eof) call next_token(lexer, token) call lexer%context%push_back(token) end do ! Flag all incomplete inline table and array scopes for the parser if (lexer%top > stack_top) then do it = lexer%top, stack_top + 1, -1 select case(lexer%stack(it)%scope) case(lexer_scope%table, lexer_scope%array) lexer%context%token(lexer%stack(it)%token)%kind = token_kind%unclosed end select end do end if end subroutine fill_buffer !> Actually generate the next token, unbuffered version subroutine next_token(lexer, token) !> Instance of the lexer class(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token integer :: prev, pos ! Consume current token lexer%pos = lexer%pos + token%last - token%first + 1 prev = lexer%pos pos = lexer%pos ! If lexer is exhausted, return EOF as early as possible if (pos > len(lexer%chunk)) then call pop(lexer, lexer_scope%equal) token = toml_token(token_kind%eof, prev, pos) return end if select case(peek(lexer, pos)) case(char_kind%hash) do while(all(peek(lexer, pos+1) /= [char_kind%carriage_return, char_kind%newline]) & & .and. pos <= len(lexer%chunk)) pos = pos + 1 end do token = toml_token(token_kind%comment, prev, pos) case(char_kind%space, char_kind%tab) do while(any(match(lexer, pos+1, [char_kind%space, char_kind%tab])) & & .and. pos <= len(lexer%chunk)) pos = pos + 1 end do token = toml_token(token_kind%whitespace, prev, pos) case(char_kind%newline) call pop(lexer, lexer_scope%equal) token = toml_token(token_kind%newline, prev, pos) case(char_kind%carriage_return) if (match(lexer, pos+1, char_kind%newline)) then pos = pos + 1 call pop(lexer, lexer_scope%equal) token = toml_token(token_kind%newline, prev, pos) else token = toml_token(token_kind%invalid, prev, pos) end if case(char_kind%dot) if (view_scope(lexer) == lexer_scope%table) then token = toml_token(token_kind%dot, prev, pos) else token = toml_token(token_kind%invalid, prev, pos) end if case(char_kind%comma) call pop(lexer, lexer_scope%equal) token = toml_token(token_kind%comma, prev, pos) case(char_kind%equal) token = toml_token(token_kind%equal, prev, pos) call push_back(lexer, lexer_scope%equal, lexer%context%top + 1) case(char_kind%lbrace) token = toml_token(token_kind%lbrace, prev, pos) call push_back(lexer, lexer_scope%table, lexer%context%top + 1) case(char_kind%rbrace) call pop(lexer, lexer_scope%equal) call pop(lexer, lexer_scope%table) token = toml_token(token_kind%rbrace, prev, pos) case(char_kind%lbracket) token = toml_token(token_kind%lbracket, prev, pos) if (any(view_scope(lexer) == [lexer_scope%equal, lexer_scope%array])) then call push_back(lexer, lexer_scope%array, lexer%context%top + 1) end if case(char_kind%rbracket) call pop(lexer, lexer_scope%array) token = toml_token(token_kind%rbracket, prev, pos) case(char_kind%squote) call next_sstring(lexer, token) case(char_kind%dquote) call next_dstring(lexer, token) case default if (view_scope(lexer) == lexer_scope%table) then call next_keypath(lexer, token) else call next_literal(lexer, token) end if end select end subroutine next_token !> Process next literal string token, can produce normal literals and multiline literals subroutine next_sstring(lexer, token) !> Instance of the lexer type(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token character(1, tfc) :: ch integer :: prev, pos, it logical :: valid prev = lexer%pos pos = lexer%pos if (all(match(lexer, [pos+1, pos+2], char_kind%squote))) then pos = pos + 3 pos = strstr(lexer%chunk(pos:), char_kind%squote3) + pos - 1 if (pos < prev + 3) then token = toml_token(token_kind%invalid, prev, len(lexer%chunk)) return end if do it = 1, 2 if (match(lexer, pos+3, char_kind%squote)) pos = pos + 1 end do valid = .true. do it = prev + 3, pos - 1 ch = peek(lexer, it) valid = valid .and. valid_string(ch) end do token = toml_token(merge(token_kind%mliteral, token_kind%invalid, valid), prev, pos+2) return end if valid = .true. do while(pos < len(lexer%chunk)) pos = pos + 1 ch = peek(lexer, pos) valid = valid .and. valid_string(ch) if (ch == char_kind%squote) exit if (ch == char_kind%newline) then pos = pos - 1 valid = .false. exit end if end do valid = valid .and. peek(lexer, pos) == char_kind%squote .and. pos /= prev token = toml_token(merge(token_kind%literal, token_kind%invalid, valid), prev, pos) end subroutine next_sstring !> Process next string token, can produce normal string and multiline string tokens subroutine next_dstring(lexer, token) !> Instance of the lexer type(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token character(1, tfc) :: ch character(*, tfc), parameter :: hexnum = "0123456789ABCDEF", valid_escape = "betnfr\""" integer :: prev, pos, expect, it, hex logical :: escape, valid, space prev = lexer%pos pos = lexer%pos hex = 0 if (all(match(lexer, [pos+1, pos+2], char_kind%dquote))) then pos = pos + 3 do it = strstr(lexer%chunk(pos:), char_kind%dquote3) pos = it + pos - 1 if (pos < prev + 3 .or. it == 0) then token = toml_token(token_kind%invalid, prev, len(lexer%chunk)) return end if if (match(lexer, pos-1, char_kind%backslash)) then pos = pos + 1 cycle end if do it = 1, 2 if (match(lexer, pos+3, char_kind%dquote)) pos = pos + 1 end do exit end do valid = .true. escape = .false. space = .false. expect = 0 do it = prev + 3, pos - 1 ch = peek(lexer, it) if (escape) then space = verify(ch, char_kind%space//char_kind%tab//& & char_kind%carriage_return//char_kind%newline) == 0 end if if (space) then escape = .false. if (ch == char_kind%newline) then if (expect > 0) expect = expect - 1 space = .false. cycle end if if (verify(ch, char_kind%space//char_kind%tab) == 0 .and. expect == 0) cycle if (ch == char_kind%carriage_return) then expect = 1 cycle end if valid = .false. space = .false. expect = 0 cycle end if valid = valid .and. valid_string(ch) if (escape) then escape = .false. space = .false. if (verify(ch, valid_escape) == 0) cycle if (ch == "x") then expect = 2 hex = it + 1 cycle end if if (ch == "u") then expect = 4 hex = pos + 1 cycle end if if (ch == "U") then expect = 8 hex = pos + 1 cycle end if valid = .false. cycle end if if (expect > 0) then expect = expect - 1 valid = valid .and. verify(ch, hexnum) == 0 if (expect == 0) valid = valid .and. verify_ucs(lexer%chunk(hex:pos)) cycle end if escape = ch == char_kind%backslash end do ! Check for any unfinished escape sequences valid = valid .and. expect == 0 .and. .not.(escape.or.space) token = toml_token(merge(token_kind%mstring, token_kind%invalid, valid), prev, pos+2) return end if valid = .true. escape = .false. expect = 0 do while(pos < len(lexer%chunk)) pos = pos + 1 ch = peek(lexer, pos) valid = valid .and. valid_string(ch) if (escape) then escape = .false. if (verify(ch, valid_escape) == 0) cycle if (ch == "x") then expect = 2 hex = it + 1 cycle end if if (ch == "u") then expect = 4 hex = pos + 1 cycle end if if (ch == "U") then expect = 8 hex = pos + 1 cycle end if valid = .false. cycle end if if (expect > 0) then expect = expect - 1 valid = valid .and. verify(ch, hexnum) == 0 if (expect == 0) valid = valid .and. verify_ucs(lexer%chunk(hex:pos)) cycle end if escape = ch == char_kind%backslash if (ch == char_kind%dquote) exit if (ch == char_kind%newline) then pos = pos - 1 valid = .false. exit end if end do valid = valid .and. peek(lexer, pos) == char_kind%dquote .and. pos /= prev token = toml_token(merge(token_kind%string, token_kind%invalid, valid), prev, pos) end subroutine next_dstring !> Validate characters in string, non-printable characters are invalid in this context pure function valid_string(ch) result(valid) character(1, tfc), intent(in) :: ch logical :: valid character(1, tfc), parameter :: x00 = achar(int(z"00")), x08 = achar(int(z"08")), & & x0b = achar(int(z"0b")), x1f = achar(int(z"1f")), x7f = achar(int(z"7f")) valid = & & .not.(x00 <= ch .and. ch <= x08) .and. & & .not.(x0b <= ch .and. ch <= x1f) .and. & & ch /= x7f end function !> Process next bare key token, produces keypath tokens. subroutine next_keypath(lexer, token) !> Instance of the lexer type(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token logical :: valid integer :: prev, pos character(1, tfc) :: ch prev = lexer%pos pos = lexer%pos ch = peek(lexer, pos) valid = (tfc_"A" <= ch .and. ch <= tfc_"Z") & & .or. (tfc_"a" <= ch .and. ch <= tfc_"z") & & .or. (verify(ch, char_kind%literal) == 0) do while(verify(peek(lexer, pos+1), terminated//char_kind%dot) > 0) pos = pos + 1 ch = peek(lexer, pos) if (tfc_"A" <= ch .and. ch <= tfc_"Z") cycle if (tfc_"a" <= ch .and. ch <= tfc_"z") cycle if (verify(ch, char_kind%literal) == 0) cycle valid = .false. cycle end do token = toml_token(merge(token_kind%keypath, token_kind%invalid, valid), prev, pos) end subroutine next_keypath !> Identify literal values, produces integer, float, boolean, and datetime tokens. subroutine next_literal(lexer, token) !> Instance of the lexer type(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token integer :: prev, pos integer, parameter :: offset(*) = [0, 1, 2, 3, 4, 5] character(1, tfc), parameter :: & & true(4) = ["t", "r", "u", "e"], false(5) = ["f", "a", "l", "s", "e"] prev = lexer%pos pos = lexer%pos select case(peek(lexer, pos)) case("t") if (match_all(lexer, pos+offset(:4), true) .and. & & verify(peek(lexer, pos+4), terminated) == 0) then token = toml_token(token_kind%bool, prev, pos+3) return end if case("f") if (match_all(lexer, pos+offset(:5), false) .and. & & verify(peek(lexer, pos+5), terminated) == 0) then token = toml_token(token_kind%bool, prev, pos+4) return end if case default call next_datetime(lexer, token) if (token%kind == token_kind%datetime) return call next_integer(lexer, token) if (token%kind == token_kind%int) return call next_float(lexer, token) if (token%kind == token_kind%float) return end select ! If the current token is invalid, advance to the next terminator do while(verify(peek(lexer, pos+1), terminated) > 0) pos = pos + 1 end do token = toml_token(token_kind%invalid, prev, pos) end subroutine next_literal !> Process integer tokens and binary, octal, and hexadecimal literals. subroutine next_integer(lexer, token) !> Instance of the lexer type(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token character(*, tfc), parameter :: toml_base(4) = [& & "0123456789abcdefABCDEF", & & "0123456789000000000000", & & "0123456700000000000000", & & "0100000000000000000000"] integer, parameter :: b10 = 2, b16 = 1, b8 = 3, b2 = 4 character(1, tfc) :: ch integer :: prev, pos, base logical :: underscore, okay prev = lexer%pos pos = lexer%pos okay = .true. underscore = .true. base = b10 if (any(match(lexer, pos, ["+", "-"]))) then pos = pos + 1 end if if (match(lexer, pos, "0")) then select case(peek(lexer, pos+1)) case("x") okay = pos == prev base = b16 pos = pos + 2 case("o") okay = pos == prev base = b8 pos = pos + 2 case("b") okay = pos == prev base = b2 pos = pos + 2 case(char_kind%space, char_kind%tab, char_kind%newline, char_kind%carriage_return, & & char_kind%hash, char_kind%rbrace, char_kind%rbracket, char_kind%comma) token = toml_token(token_kind%int, prev, pos) return case default do while(verify(peek(lexer, pos), terminated) > 0) pos = pos + 1 end do token = toml_token(token_kind%invalid, prev, pos-1) return end select end if do while(pos <= len(lexer%chunk)) ch = peek(lexer, pos) if (ch == "_") then if (underscore) then token = toml_token(token_kind%invalid, prev, pos) return end if underscore = .true. pos = pos + 1 cycle end if if (verify(ch, toml_base(base)) == 0) then pos = pos + 1 underscore = .false. cycle end if okay = okay .and. verify(ch, terminated) == 0 exit end do okay = .not.underscore .and. okay token = toml_token(merge(token_kind%int, token_kind%invalid, okay), prev, pos-1) end subroutine next_integer !> Process float tokens. subroutine next_float(lexer, token) !> Instance of the lexer type(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token integer :: prev, pos logical :: plus_minus, underscore, point, expo, okay, zero, first character(1, tfc) :: ch integer, parameter :: offset(*) = [0, 1, 2] character(1, tfc), parameter :: nan(3) = ["n", "a", "n"], inf(3) = ["i", "n", "f"] prev = lexer%pos pos = lexer%pos point = .false. expo = .false. zero = .false. first = .true. underscore = .true. plus_minus = any(match(lexer, pos, ["+", "-"])) if (plus_minus) pos = pos + 1 if (match_all(lexer, pos+offset, nan) .and. & & verify(peek(lexer, pos+3), terminated) == 0) then token = toml_token(token_kind%float, prev, pos+2) return end if if (match_all(lexer, pos+offset, inf) .and. & & verify(peek(lexer, pos+3), terminated) == 0) then token = toml_token(token_kind%float, prev, pos+2) return end if do while(pos <= len(lexer%chunk)) ch = peek(lexer, pos) if (ch == "_") then if (underscore) then token = toml_token(token_kind%invalid, prev, pos) return end if underscore = .true. pos = pos + 1 cycle end if if (ch == ".") then if (point .or. expo .or. underscore) then token = toml_token(token_kind%invalid, prev, pos) return end if zero = .false. underscore = .true. point = .true. pos = pos + 1 cycle end if if (ch == "e" .or. ch == "E") then if (expo .or. underscore) then token = toml_token(token_kind%invalid, prev, pos) return end if zero = .false. underscore = .true. expo = .true. pos = pos + 1 cycle end if if (ch == "+" .or. ch == "-") then if (.not.any(match(lexer, pos-1, ["e", "E"]))) then token = toml_token(token_kind%invalid, prev, pos) return end if underscore = .true. pos = pos + 1 cycle end if if (verify(ch, "0123456789") == 0) then if (zero) then token = toml_token(token_kind%invalid, prev, pos) return end if zero = first .and. ch == "0" first = .false. pos = pos + 1 underscore = .false. cycle end if exit end do okay = .not.underscore .and. (expo .or. point) token = toml_token(merge(token_kind%float, token_kind%invalid, okay), prev, pos-1) end subroutine next_float !> Find the next datetime expression subroutine next_datetime(lexer, token) !> Instance of the lexer type(toml_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token logical :: has_date, has_time, has_millisec, has_local, okay, has_seconds integer :: prev, pos, it, time_len integer, parameter :: offset(*) = [(it, it = 0, 10)], & & offset_date = 10, offset_time = 8, offset_time_no_sec = 5, offset_local = 6 character(*, tfc), parameter :: num = "0123456789" prev = lexer%pos pos = lexer%pos has_date = valid_date(peek(lexer, pos+offset(:offset_date))) if (has_date) then if (verify(peek(lexer, pos+offset_date), "Tt ") == 0 & & .and. pos + offset_date < len(lexer%chunk) & & .and. verify(peek(lexer, pos+offset_date+1), num) == 0) then pos = pos + offset_date + 1 end if end if ! Try to validate time - first with 8 characters (HH:MM:SS), then 5 (HH:MM) call valid_time(peek(lexer, pos+offset(:offset_time)), has_time, has_seconds) if (has_time) then if (has_seconds) then time_len = offset_time else time_len = offset_time_no_sec end if pos = pos + time_len - 1 if (match(lexer, pos+1, char_kind%dot)) then it = 1 do while(verify(peek(lexer, pos+it+1), num) == 0) it = it + 1 end do has_millisec = it > 1 if (.not.has_millisec) then token = toml_token(token_kind%invalid, prev, prev) return end if pos = pos + it end if has_local = valid_local(peek(lexer, pos+offset(:offset_local)+1)) if (has_local) then if (.not.has_date) then token = toml_token(token_kind%invalid, prev, prev) return end if pos = pos + offset_local else if (verify(peek(lexer, pos+1), "zZ") == 0) then pos = pos + 1 end if end if if (.not.(has_time.or.has_date)) then token = toml_token(token_kind%invalid, prev, prev) return end if if (.not.has_time.and.has_date) pos = pos + offset_date - 1 okay = verify(peek(lexer, pos+1), terminated) == 0 .and. pos <= len(lexer%chunk) token = toml_token(merge(token_kind%datetime, token_kind%invalid, okay), prev, pos) end subroutine next_datetime !> Validate a string as date pure function valid_date(string) result(valid) !> Input string, 10 characters character(1, tfc), intent(in) :: string(:) !> Valid date logical :: valid integer :: it, val character(*, tfc), parameter :: num = "0123456789" integer :: year, month, day, mday logical :: leap valid = .false. if (any(string([5, 8]) /= "-")) return year = 0 do it = 1, 4 val = scan(num, string(it)) - 1 if (val < 0) return year = year * 10 + val end do month = 0 do it = 6, 7 val = scan(num, string(it)) - 1 if (val < 0) return month = month * 10 + val end do day = 0 do it = 9, 10 val = scan(num, string(it)) - 1 if (val < 0) return day = day * 10 + val end do mday = 0 select case(month) case(1, 3, 5, 7, 8, 10, 12) mday = 31 case(2) leap = mod(year, 4) == 0 .and. (mod(year, 100) /= 0 .or. mod(year, 400) == 0) mday = merge(29, 28, leap) case(4, 6, 9, 11) mday = 30 end select valid = day >= 1 .and. day <= mday end function valid_date !> Validate a string as time (HH:MM or HH:MM:SS) subroutine valid_time(string, valid, has_seconds) !> Input string, 5 characters (HH:MM) or 8 characters (HH:MM:SS) character(1, tfc), intent(in) :: string(:) !> Valid time logical, intent(out) :: valid !> Whether the time has seconds logical, intent(out) :: has_seconds integer :: it, val character(*, tfc), parameter :: num = "0123456789" integer :: hour, minute, second valid = .false. has_seconds = .false. if (string(3) /= ":") return hour = 0 do it = 1, 2 val = scan(num, string(it)) - 1 if (val < 0) return hour = hour * 10 + val end do minute = 0 do it = 4, 5 val = scan(num, string(it)) - 1 if (val < 0) return minute = minute * 10 + val end do ! Check for seconds (optional in TOML 1.1) if (size(string) >= 8 .and. string(6) == ":") then second = 0 do it = 7, 8 val = scan(num, string(it)) - 1 if (val < 0) return second = second * 10 + val end do if (second < 0 .or. second >= 60) return has_seconds = .true. end if valid = minute >= 0 .and. minute < 60 & & .and. hour >= 0 .and. hour < 24 end subroutine valid_time !> Validate a string as timezone function valid_local(string) result(valid) !> Input string, 6 characters character(1, tfc), intent(in) :: string(:) !> Valid timezone logical :: valid integer :: it, val character(*, tfc), parameter :: num = "0123456789" integer :: hour, minute valid = .false. if (string(4) /= ":" .or. all(string(1) /= ["+", "-"])) return hour = 0 do it = 2, 3 val = scan(num, string(it)) - 1 if (val < 0) return hour = hour * 10 + val end do minute = 0 do it = 5, 6 val = scan(num, string(it)) - 1 if (val < 0) return minute = minute * 10 + val end do valid = minute >= 0 .and. minute < 60 & & .and. hour >= 0 .and. hour < 24 end function valid_local !> Show current character elemental function peek(lexer, pos) result(ch) !> Instance of the lexer type(toml_lexer), intent(in) :: lexer !> Position to fetch character from integer, intent(in) :: pos !> Character found character(1, tfc) :: ch if (pos <= len(lexer%chunk)) then ch = lexer%chunk(pos:pos) else ch = char_kind%space end if end function peek !> Compare a character elemental function match(lexer, pos, kind) !> Instance of the lexer type(toml_lexer), intent(in) :: lexer !> Position to fetch character from integer, intent(in) :: pos !> Character to compare against character(1, tfc), intent(in) :: kind !> Characters match logical :: match match = peek(lexer, pos) == kind end function match !> Compare a set of characters pure function match_all(lexer, pos, kind) result(match) !> Instance of the lexer type(toml_lexer), intent(in) :: lexer !> Position to fetch character from integer, intent(in) :: pos(:) !> Character to compare against character(1, tfc), intent(in) :: kind(:) !> Characters match logical :: match match = all(peek(lexer, pos) == kind) end function match_all pure function strstr(string, pattern) result(res) character(*, tfc), intent(in) :: string character(*, tfc), intent(in) :: pattern integer :: lps_array(len(pattern)) integer :: res, s_i, p_i, length_string, length_pattern res = 0 length_string = len(string) length_pattern = len(pattern) if (length_pattern > 0 .and. length_pattern <= length_string) then lps_array = compute_lps(pattern) s_i = 1 p_i = 1 do while(s_i <= length_string) if (string(s_i:s_i) == pattern(p_i:p_i)) then if (p_i == length_pattern) then res = s_i - length_pattern + 1 exit end if s_i = s_i + 1 p_i = p_i + 1 else if (p_i > 1) then p_i = lps_array(p_i - 1) + 1 else s_i = s_i + 1 end if end do end if contains pure function compute_lps(string) result(lps_array) character(*, tfc), intent(in) :: string integer :: lps_array(len(string)) integer :: i, j, length_string length_string = len(string) if (length_string > 0) then lps_array(1) = 0 i = 2 j = 1 do while (i <= length_string) if (string(j:j) == string(i:i)) then lps_array(i) = j i = i + 1 j = j + 1 else if (j > 1) then j = lps_array(j - 1) + 1 else lps_array(i) = 0 i = i + 1 end if end do end if end function compute_lps end function strstr !> Extract string value of token, works for keypath, string, multiline string, literal, !> and mulitline literal tokens. subroutine extract_string(lexer, token, string) !> Instance of the lexer class(toml_lexer), intent(in) :: lexer !> Token to extract string value from type(toml_token), intent(in) :: token !> String value of token character(len=:), allocatable, intent(out) :: string integer :: it, length logical :: escape, leading_newline character(1, tfc) :: ch length = token%last - token%first + 1 select case(token%kind) case(token_kind%string) string = "" escape = .false. it = token%first + 1 do while(it <= token%last - 1) ch = peek(lexer, it) if (escape) then escape = .false. select case(ch) case("""", "\"); string = string // ch case("b"); string = string // TOML_BACKSPACE case("e"); string = string // TOML_ESC case("t"); string = string // TOML_TABULATOR case("n"); string = string // TOML_NEWLINE case("r"); string = string // TOML_CARRIAGE_RETURN case("f"); string = string // TOML_FORMFEED case("x"); string = string // convert_ucs(lexer%chunk(it+1:it+2)); it = it + 3 case("u"); string = string // convert_ucs(lexer%chunk(it+1:it+4)); it = it + 5 case("U"); string = string // convert_ucs(lexer%chunk(it+1:it+8)); it = it + 9 end select else escape = ch == char_kind%backslash if (.not.escape) string = string // ch end if it = it + 1 end do case(token_kind%mstring) leading_newline = peek(lexer, token%first+3) == char_kind%newline string = "" escape = .false. it = token%first + merge(4, 3, leading_newline) do while(it <= token%last - 3) ch = peek(lexer, it) if (escape) then escape = .false. select case(ch) case("""", "\"); string = string // ch case("b"); string = string // TOML_BACKSPACE case("e"); string = string // TOML_ESC case("t"); string = string // TOML_TABULATOR case("n"); string = string // TOML_NEWLINE case("r"); string = string // TOML_CARRIAGE_RETURN case("f"); string = string // TOML_FORMFEED case("x"); string = string // convert_ucs(lexer%chunk(it+1:it+2)); it = it + 3 case("u"); string = string // convert_ucs(lexer%chunk(it+1:it+4)); it = it + 5 case("U"); string = string // convert_ucs(lexer%chunk(it+1:it+8)); it = it + 9 case(char_kind%space, char_kind%tab, char_kind%carriage_return) escape = .true. case(char_kind%newline) continue end select else escape = ch == char_kind%backslash if (.not.escape) string = string // ch end if it = it + 1 end do case(token_kind%literal) allocate(character(length - 2)::string) string = lexer%chunk(token%first+1:token%last-1) case(token_kind%mliteral) leading_newline = peek(lexer, token%first+3) == char_kind%newline allocate(character(length - merge(7, 6, leading_newline))::string) string = lexer%chunk(token%first+merge(4, 3, leading_newline):token%last-3) case(token_kind%keypath) allocate(character(length)::string) string = lexer%chunk(token%first:token%last) end select end subroutine extract_string !> Extract integer value of token subroutine extract_integer(lexer, token, val) !> Instance of the lexer class(toml_lexer), intent(in) :: lexer !> Token to extract integer value from type(toml_token), intent(in) :: token !> Integer value of token integer(tfi), intent(out) :: val integer :: first, base, it, tmp logical :: minus character(1, tfc) :: ch character(*, tfc), parameter :: num = "0123456789abcdef" if (token%kind /= token_kind%int) return val = 0 base = 10 first = token%first if (any(peek(lexer, first) == ["+", "-"])) first = first + 1 if (peek(lexer, first) == "0") then select case(peek(lexer, first + 1)) case("x") first = first + 2 base = 16 case("o") first = first + 2 base = 8 case("b") first = first + 2 base = 2 case default return end select end if minus = match(lexer, token%first, char_kind%minus) do it = first, token%last ch = peek(lexer, it) if ("A" <= ch .and. ch <= "Z") ch = achar(iachar(ch) - iachar("A") + iachar("a")) tmp = scan(num(:abs(base)), ch) - 1 if (tmp < 0) cycle val = val * base + merge(-tmp, tmp, minus) end do end subroutine extract_integer !> Extract floating point value of token subroutine extract_float(lexer, token, val) ! Not useable since unsupported with GFortran on some platforms (MacOS/ppc) ! use, intrinsic :: ieee_arithmetic, only : ieee_value, ieee_quite_nan, & ! & ieee_positive_inf, ieee_negative_inf !> Instance of the lexer class(toml_lexer), intent(in) :: lexer !> Token to extract floating point value from type(toml_token), intent(in) :: token !> Floating point value of token real(tfr), intent(out) :: val integer :: first, it, ic character(len=token%last - token%first + 1) :: buffer character(1, tfc) :: ch if (token%kind /= token_kind%float) return first = token%first if (any(peek(lexer, first) == ["+", "-"])) first = first + 1 if (match(lexer, first, "n")) then ! val = ieee_value(val, ieee_quite_nan) buffer = "NaN" read(buffer, *, iostat=ic) val return end if if (match(lexer, first, "i")) then ! val = ieee_value(val, ieee_positive_inf) buffer = "Inf" read(buffer, *, iostat=ic) val if (match(lexer, token%first, char_kind%minus)) val = -val return end if ! ival = 0 ! idot = 0 ! ! do it = first, token%last ! ch = peek(lexer, it) ! if (any(ch == [".", "e", "E"])) exit ! tmp = scan(num(:base), ch) - 1 ! if (tmp < 0) cycle ! ival = ival * base + tmp ! end do ! first = it ! ! if (ch == ".") then ! idot = 0 ! do it = first, token%last ! ch = peek(lexer, it) ! if (any(ch == ["e", "E"])) exit ! tmp = scan(num(:base), ch) - 1 ! if (tmp < 0) cycle ! idot = idot + 1 ! ival = ival * base + tmp ! end do ! first = it ! end if ! ! expo = 0 ! if (any(ch == ["e", "E"])) then ! first = first + 1 ! do it = first, token%last ! ch = peek(lexer, it) ! tmp = scan(num(:base), ch) - 1 ! if (tmp < 0) cycle ! expo = expo * base + tmp ! end do ! if (match(lexer, first, char_kind%minus)) expo = -expo ! end if ! expo = expo - idot ! val = ival * 10.0_tfr ** expo ! FIXME ! ! if (match(lexer, token%first, char_kind%minus)) val = -val ic = 0 do it = token%first, token%last ch = peek(lexer, it) if (ch == "_") cycle ic = ic + 1 buffer(ic:ic) = ch end do read(buffer(:ic), *, iostat=it) val end subroutine extract_float !> Extract boolean value of token subroutine extract_bool(lexer, token, val) !> Instance of the lexer class(toml_lexer), intent(in) :: lexer !> Token to extract boolean value from type(toml_token), intent(in) :: token !> Boolean value of token logical, intent(out) :: val if (token%kind /= token_kind%bool) return val = peek(lexer, token%first) == "t" end subroutine extract_bool !> Extract datetime value of token subroutine extract_datetime(lexer, token, val) !> Instance of the lexer class(toml_lexer), intent(in) :: lexer !> Token to extract datetime value from type(toml_token), intent(in) :: token !> Datetime value of token type(toml_datetime), intent(out) :: val if (token%kind /= token_kind%datetime) return val = toml_datetime(lexer%chunk(token%first:token%last)) end subroutine extract_datetime !> Push a new scope onto the lexer stack and record the token pure subroutine push_back(lexer, scope, token) type(toml_lexer), intent(inout) :: lexer integer, intent(in) :: scope integer, intent(in) :: token lexer%top = lexer%top + 1 if (lexer%top > size(lexer%stack)) call resize(lexer%stack) lexer%stack(lexer%top) = stack_item(scope, token) end subroutine push_back !> Pop a scope from the lexer stack in case the topmost scope matches the requested scope subroutine pop(lexer, scope) type(toml_lexer), intent(inout) :: lexer integer, intent(in) :: scope if (lexer%top > 0) then if (lexer%stack(lexer%top)%scope == scope) lexer%top = lexer%top - 1 end if end subroutine pop !> Peek at the topmost scope on the lexer stack pure function view_scope(lexer) result(scope) type(toml_lexer), intent(in) :: lexer integer :: scope if (lexer%top > 0) then scope = lexer%stack(lexer%top)%scope else scope = lexer_scope%table end if end function view_scope !> Reallocate list of scopes pure subroutine resize_scope(var, n) !> Instance of the array to be resized type(stack_item), allocatable, intent(inout) :: var(:) !> Dimension of the final array size integer, intent(in), optional :: n type(stack_item), allocatable :: tmp(:) integer :: this_size, new_size integer, parameter :: initial_size = 8 if (allocated(var)) then this_size = size(var, 1) call move_alloc(var, tmp) else this_size = initial_size end if if (present(n)) then new_size = n else new_size = this_size + this_size/2 + 1 end if allocate(var(new_size)) if (allocated(tmp)) then this_size = min(size(tmp, 1), size(var, 1)) var(:this_size) = tmp(:this_size) deallocate(tmp) end if end subroutine resize_scope !> Extract information about the source subroutine get_info(lexer, meta, output) !> Instance of the lexer class(toml_lexer), intent(in) :: lexer !> Query about the source character(*, tfc), intent(in) :: meta !> Metadata about the source character(:, tfc), allocatable, intent(out) :: output select case(meta) case("source") output = lexer%chunk // TOML_NEWLINE case("filename") if (allocated(lexer%filename)) output = lexer%filename end select end subroutine get_info function hex_to_int(hex) result(val) character(*, tfc), intent(in) :: hex integer(tfi) :: val integer :: i character(1, tfc) :: ch character(*, tfc), parameter :: hex_digits = "0123456789abcdef" val = 0_tfi do i = 1, len(hex) ch = hex(i:i) if ("A" <= ch .and. ch <= "Z") ch = achar(iachar(ch) - iachar("A") + iachar("a")) val = val * 16 + max(index(hex_digits, ch) - 1, 0) end do end function hex_to_int function verify_ucs(escape) result(valid) character(*, tfc), intent(in) :: escape logical :: valid integer(tfi) :: code code = hex_to_int(escape) valid = code > 0 .and. code < int(z"7FFFFFFF", tfi) & & .and. (code < int(z"d800", tfi) .or. code > int(z"dfff", tfi)) & & .and. (code < int(z"fffe", tfi) .or. code > int(z"ffff", tfi)) end function verify_ucs function convert_ucs(escape) result(str) character(*, tfc), intent(in) :: escape character(:, tfc), allocatable :: str integer(tfi) :: code code = hex_to_int(escape) select case(code) case(int(z"00000000", tfi):int(z"0000007f", tfi)) str = achar(code, kind=tfc) case(int(z"00000080", tfi):int(z"000007ff", tfi)) str = & achar(ior(int(z"c0", tfi), ishft(code, -6)), kind=tfc) // & achar(ior(int(z"80", tfi), iand(code, int(z"3f", tfi))), kind=tfc) case(int(z"00000800", tfi):int(z"0000ffff", tfi)) str = & achar(ior(int(z"e0", tfi), ishft(code, -12)), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -6), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(code, int(z"3f", tfi))), kind=tfc) case(int(z"00010000", tfi):int(z"001fffff", tfi)) str = & achar(ior(int(z"f0", tfi), ishft(code, -18)), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -12), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -6), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(code, int(z"3f", tfi))), kind=tfc) case(int(z"00200000", tfi):int(z"03ffffff", tfi)) str = & achar(ior(int(z"f8", tfi), ishft(code, -24)), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -18), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -12), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -6), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(code, int(z"3f", tfi))), kind=tfc) case(int(z"04000000", tfi):int(z"7fffffff", tfi)) str = & achar(ior(int(z"fc", tfi), ishft(code, -30)), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -24), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -18), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -12), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(ishft(code, -6), int(z"3f", tfi))), kind=tfc) // & achar(ior(int(z"80", tfi), iand(code, int(z"3f", tfi))), kind=tfc) end select end function convert_ucs end module tomlf_de_lexer fortran-toml-0.5.0/src/tomlf/de/abc.f900000664000175000017500000001144215201541453017673 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Defines the abstract base class which is implemented by the TOML lexer. module tomlf_de_abc use tomlf_constants, only : tfc, tfi, tfr use tomlf_datetime, only : toml_datetime use tomlf_de_token, only : toml_token implicit none private public :: abstract_lexer !> Abstract base class for TOML lexers. type, abstract :: abstract_lexer contains !> Obtain the next token procedure(next), deferred :: next !> Extract a token generic :: extract => & & extract_string, extract_integer, extract_float, extract_bool, extract_datetime !> Extract a string from a token procedure(extract_string), deferred :: extract_string !> Extract an integer from a token procedure(extract_integer), deferred :: extract_integer !> Extract a float from a token procedure(extract_float), deferred :: extract_float !> Extract a boolean from a token procedure(extract_bool), deferred :: extract_bool !> Extract a timestamp from a token procedure(extract_datetime), deferred :: extract_datetime !> Get information about the source procedure(get_info), deferred :: get_info end type abstract_lexer abstract interface !> Advance the lexer to the next token. subroutine next(lexer, token) import :: abstract_lexer, toml_token !> Instance of the lexer class(abstract_lexer), intent(inout) :: lexer !> Current lexeme type(toml_token), intent(inout) :: token end subroutine next !> Extract string value of token, works for keypath, string, multiline string, literal, !> and mulitline literal tokens. subroutine extract_string(lexer, token, string) import :: abstract_lexer, toml_token, tfc !> Instance of the lexer class(abstract_lexer), intent(in) :: lexer !> Token to extract string value from type(toml_token), intent(in) :: token !> String value of token character(:, tfc), allocatable, intent(out) :: string end subroutine extract_string !> Extract integer value of token subroutine extract_integer(lexer, token, val) import :: abstract_lexer, toml_token, tfi !> Instance of the lexer class(abstract_lexer), intent(in) :: lexer !> Token to extract integer value from type(toml_token), intent(in) :: token !> Integer value of token integer(tfi), intent(out) :: val end subroutine extract_integer !> Extract floating point value of token subroutine extract_float(lexer, token, val) import :: abstract_lexer, toml_token, tfr !> Instance of the lexer class(abstract_lexer), intent(in) :: lexer !> Token to extract floating point value from type(toml_token), intent(in) :: token !> Floating point value of token real(tfr), intent(out) :: val end subroutine extract_float !> Extract boolean value of token subroutine extract_bool(lexer, token, val) import :: abstract_lexer, toml_token !> Instance of the lexer class(abstract_lexer), intent(in) :: lexer !> Token to extract boolean value from type(toml_token), intent(in) :: token !> Boolean value of token logical, intent(out) :: val end subroutine extract_bool !> Extract datetime value of token subroutine extract_datetime(lexer, token, val) import :: abstract_lexer, toml_token, toml_datetime !> Instance of the lexer class(abstract_lexer), intent(in) :: lexer !> Token to extract datetime value from type(toml_token), intent(in) :: token !> Datetime value of token type(toml_datetime), intent(out) :: val end subroutine extract_datetime !> Extract information about the source subroutine get_info(lexer, meta, output) import :: abstract_lexer, tfc !> Instance of the lexer class(abstract_lexer), intent(in) :: lexer !> Query about the source character(*, tfc), intent(in) :: meta !> Metadata about the source character(:, tfc), allocatable, intent(out) :: output end subroutine get_info end interface end module tomlf_de_abc fortran-toml-0.5.0/src/tomlf/de/token.f900000664000175000017500000001167115201541453020272 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Provides a definition for a token module tomlf_de_token implicit none private public :: toml_token, stringify, token_kind, resize !> Possible token kinds type :: enum_token !> Invalid token found integer :: invalid = -1 !> End of file integer :: eof = -2 !> Unclosed group from inline table or array integer :: unclosed = -3 !> Whitespace (space, tab) integer :: whitespace = 0 !> Newline character (\r\n, \n) integer :: newline = 1 !> Comments (#) integer :: comment = 2 !> Separator in table path (.) integer :: dot = 3 !> Separator in inline arrays and inline tables (,) integer :: comma = 4 !> Separator in key-value pairs (=) integer :: equal = 5 !> Beginning of an inline table ({) integer :: lbrace = 6 !> End of an inline table (}) integer :: rbrace = 7 !> Beginning of an inline array or table header ([) integer :: lbracket = 8 !> End of an inline array or table header (]) integer :: rbracket = 9 !> String literal integer :: string = 10 !> String literal integer :: mstring = 11 !> String literal integer :: literal = 12 !> String literal integer :: mliteral = 13 !> String literal integer :: keypath = 14 !> Floating point value integer :: float = 15 !> Integer value integer :: int = 16 !> Boolean value integer :: bool = 17 !> Datetime value integer :: datetime = 18 !> Absence of value integer :: nil = 19 end type enum_token !> Actual enumerator for token kinds type(enum_token), parameter :: token_kind = enum_token() !> Token containing type :: toml_token !> Kind of token integer :: kind = token_kind%newline !> Starting position of the token in character stream integer :: first = 0 !> Last position of the token in character stream integer :: last = 0 !> Identifier for the chunk index in case of buffered reading integer :: chunk = 0 end type toml_token !> Reallocate a list of tokens interface resize module procedure :: resize_token end interface contains !> Reallocate list of tokens pure subroutine resize_token(var, n) !> Instance of the array to be resized type(toml_token), allocatable, intent(inout) :: var(:) !> Dimension of the final array size integer, intent(in), optional :: n type(toml_token), allocatable :: tmp(:) integer :: this_size, new_size integer, parameter :: initial_size = 8 if (allocated(var)) then this_size = size(var, 1) call move_alloc(var, tmp) else this_size = initial_size end if if (present(n)) then new_size = n else new_size = this_size + this_size/2 + 1 end if allocate(var(new_size)) if (allocated(tmp)) then this_size = min(size(tmp, 1), size(var, 1)) var(:this_size) = tmp(:this_size) deallocate(tmp) end if end subroutine resize_token !> Represent a token as string pure function stringify(token) result(str) !> Token to represent as string type(toml_token), intent(in) :: token !> String representation of token character(len=:), allocatable :: str select case(token%kind) case default; str = "unknown" case(token_kind%invalid); str = "invalid sequence" case(token_kind%eof); str = "end of file" case(token_kind%unclosed); str = "unclosed group" case(token_kind%whitespace); str = "whitespace" case(token_kind%comment); str = "comment" case(token_kind%newline); str = "newline" case(token_kind%dot); str = "dot" case(token_kind%comma); str = "comma" case(token_kind%equal); str = "equal" case(token_kind%lbrace); str = "opening brace" case(token_kind%rbrace); str = "closing brace" case(token_kind%lbracket); str = "opening bracket" case(token_kind%rbracket); str = "closing bracket" case(token_kind%string); str = "string" case(token_kind%mstring); str = "multiline string" case(token_kind%literal); str = "literal" case(token_kind%mliteral); str = "multiline-literal" case(token_kind%keypath); str = "keypath" case(token_kind%int); str = "integer" case(token_kind%float); str = "float" case(token_kind%bool); str = "bool" case(token_kind%datetime); str = "datetime" case(token_kind%nil); str = "nil" end select end function stringify end module tomlf_de_token fortran-toml-0.5.0/src/tomlf/de/CMakeLists.txt0000664000175000017500000000135215201541453021365 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set(dir "${CMAKE_CURRENT_SOURCE_DIR}") list( APPEND srcs "${dir}/abc.f90" "${dir}/context.f90" "${dir}/lexer.f90" "${dir}/parser.f90" "${dir}/token.f90" ) set(srcs "${srcs}" PARENT_SCOPE) fortran-toml-0.5.0/src/tomlf/utils/0000775000175000017500000000000015201541453017374 5ustar alastairalastairfortran-toml-0.5.0/src/tomlf/utils/meson.build0000664000175000017500000000111315201541453021532 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. srcs += files( 'io.f90', 'sort.f90', ) fortran-toml-0.5.0/src/tomlf/utils/sort.f900000664000175000017500000000721215201541453020705 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Sorting algorithms to work with hash maps module tomlf_utils_sort use tomlf_type_value, only : toml_key implicit none private public :: sort, compare_less !> Create overloaded interface for export interface sort module procedure :: sort_keys end interface abstract interface !> Define order relation between two TOML keys pure function compare_less(lhs, rhs) result(less) import :: toml_key !> Left hand side TOML key in comparison type(toml_key), intent (in) :: lhs !> Right hand side TOML key in comparison type(toml_key), intent (in) :: rhs !> Comparison result logical :: less end function compare_less end interface contains !> Entry point for sorting algorithm pure subroutine sort_keys(list, idx, compare) !> List of TOML keys to be sorted type(toml_key), intent(inout) :: list(:) !> Optionally, mapping from unsorted list to sorted list integer, intent(out), optional :: idx(:) !> Function implementing the order relation between two TOML keys procedure(compare_less), optional :: compare integer :: low, high, i type(toml_key), allocatable :: sorted(:) integer, allocatable :: indexarray(:) low = 1 high = size(list) allocate(sorted, source=list) allocate(indexarray(high), source=[(i, i=low, high)]) if (present(compare)) then call quicksort(sorted, indexarray, low, high, compare) else call quicksort(sorted, indexarray, low, high, compare_keys_less) end if do i = low, high list(i) = sorted(indexarray(i)) end do if (present(idx)) then idx = indexarray end if end subroutine sort_keys !> Actual quick sort implementation pure recursive subroutine quicksort(list, idx, low, high, less) type(toml_key), intent(inout) :: list(:) integer, intent(inout) :: idx(:) integer, intent(in) :: low, high procedure(compare_less) :: less integer :: i, last integer :: pivot if (low < high) then call swap(idx(low), idx((low + high)/2)) last = low do i = low + 1, high if (less(list(idx(i)), list(idx(low)))) then last = last + 1 call swap(idx(last), idx(i)) end if end do call swap(idx(low), idx(last)) pivot = last call quicksort(list, idx, low, pivot - 1, less) call quicksort(list, idx, pivot + 1, high, less) end if end subroutine quicksort !> Swap two integer values pure subroutine swap(lhs, rhs) integer, intent(inout) :: lhs integer, intent(inout) :: rhs integer :: tmp tmp = lhs lhs = rhs rhs = tmp end subroutine swap !> Default comparison between two TOML keys pure function compare_keys_less(lhs, rhs) result(less) type(toml_key), intent (in) :: lhs type(toml_key), intent (in) :: rhs logical :: less less = lhs%key < rhs%key end function compare_keys_less end module tomlf_utils_sort fortran-toml-0.5.0/src/tomlf/utils/io.f900000664000175000017500000000467215201541453020334 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Utilities for handling input and output operations module tomlf_utils_io use tomlf_constants, only : tfc implicit none private public :: read_whole_file, read_whole_line contains !> Read a whole file into an array of characters subroutine read_whole_file(filename, string, stat) !> File to read character(*, tfc), intent(in) :: filename !> Array of characters representing the file character(:, tfc), allocatable, intent(out) :: string !> Error status integer, intent(out) :: stat integer :: io, length open(file=filename, & & status="old", & & access="stream", & & position="append", & & newunit=io, & & iostat=stat) if (stat == 0) then inquire(unit=io, pos=length) allocate(character(length-1, tfc) :: string, stat=stat) end if if (stat == 0) then read(io, pos=1, iostat=stat) string(:length-1) end if if (stat == 0) then close(io) end if end subroutine read_whole_file !> Read a whole line from a formatted unit into a deferred length character variable subroutine read_whole_line(io, string, stat) !> Formatted IO unit integer, intent(in) :: io !> Line to read character(:, tfc), allocatable, intent(out) :: string !> Status of operation integer, intent(out) :: stat integer, parameter :: bufsize = 4096 character(bufsize, tfc) :: buffer, msg integer :: chunk logical :: opened if (io /= -1) then inquire(unit=io, opened=opened) else opened = .false. end if if (opened) then open(unit=io, pad="yes", iostat=stat) else stat = 1 msg = "Unit is not connected" end if string = "" do while (stat == 0) read(io, '(a)', advance='no', iostat=stat, size=chunk) buffer if (stat > 0) exit string = string // buffer(:chunk) end do if (is_iostat_eor(stat)) stat = 0 end subroutine read_whole_line end module tomlf_utils_io fortran-toml-0.5.0/src/tomlf/utils/CMakeLists.txt0000664000175000017500000000124615201541453022137 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set(dir "${CMAKE_CURRENT_SOURCE_DIR}") list( APPEND srcs "${dir}/io.f90" "${dir}/sort.f90" ) set(srcs "${srcs}" PARENT_SCOPE) fortran-toml-0.5.0/src/tomlf/ser.f900000664000175000017500000003645015201541453017355 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> TOML serialization module !> !> This module provides interfaces for serializing TOML data structures !> back to TOML format. The primary interfaces are: !> !> - [[toml_dump]]: Write a TOML table to a file or I/O unit !> - [[toml_dumps]]: Serialize a TOML table to a string !> - [[toml_serialize]]: Low-level serialization using the visitor pattern !> !> The [[toml_serializer]] type implements the visitor pattern and can be !> used directly for custom serialization workflows. module tomlf_ser use tomlf_constants, only : tfc, tfi, tfr, tfout, toml_type use tomlf_datetime, only : toml_datetime, to_string use tomlf_error, only : toml_error, toml_stat, make_error use tomlf_type, only : toml_value, toml_visitor, toml_key, toml_table, & & toml_array, toml_keyval, is_array_of_tables, len use tomlf_utils, only : to_string, toml_escape_string implicit none private public :: toml_serializer, new_serializer, new public :: toml_dump, toml_dumps, toml_serialize interface toml_dumps module procedure :: toml_dump_to_string end interface toml_dumps interface toml_dump module procedure :: toml_dump_to_file module procedure :: toml_dump_to_unit end interface toml_dump !> Configuration for JSON serializer type :: toml_ser_config !> Indentation character(len=:), allocatable :: indent end type toml_ser_config !> TOML serializer to produduce a TOML document from a datastructure type, extends(toml_visitor) :: toml_serializer private !> Output string character(:), allocatable :: output !> Configuration for serializer type(toml_ser_config) :: config = toml_ser_config() !> Special mode for printing array of tables logical, private :: array_of_tables = .false. !> Special mode for printing inline arrays logical, private :: inline_array = .false. !> Top of the key stack integer, private :: top = 0 !> Key stack to create table headers type(toml_key), allocatable, private :: stack(:) contains !> Visit a TOML value procedure :: visit end type toml_serializer !> Create standard constructor interface toml_serializer module procedure :: new_serializer_func end interface toml_serializer !> Overloaded constructor for TOML serializers interface new module procedure :: new_serializer end interface !> Initial size of the key path stack integer, parameter :: initial_size = 8 contains !> Serialize a JSON value to a string and return it. !> !> In case of an error this function will invoke an error stop. function toml_serialize(val, config) result(string) !> TOML value to visit class(toml_value), intent(inout) :: val !> Configuration for serializer type(toml_ser_config), intent(in), optional :: config !> Serialized JSON value character(len=:), allocatable :: string type(toml_error), allocatable :: error call toml_dumps(val, string, error, config=config) if (allocated(error)) then print '(a)', "Error: " // error%message error stop 1 end if end function toml_serialize !> Create a string representing the JSON value subroutine toml_dump_to_string(val, string, error, config) !> TOML value to visit class(toml_value), intent(inout) :: val !> Formatted unit to write to character(:), allocatable, intent(out) :: string !> Error handling type(toml_error), allocatable, intent(out) :: error !> Configuration for serializer type(toml_ser_config), intent(in), optional :: config type(toml_serializer) :: ser ser = toml_serializer(config=config) call val%accept(ser) string = ser%output end subroutine toml_dump_to_string !> Write string representation of JSON value to a connected formatted unit subroutine toml_dump_to_unit(val, io, error, config) !> TOML value to visit class(toml_value), intent(inout) :: val !> Formatted unit to write to integer, intent(in) :: io !> Error handling type(toml_error), allocatable, intent(out) :: error !> Configuration for serializer type(toml_ser_config), intent(in), optional :: config character(len=:), allocatable :: string character(512) :: msg integer :: stat call toml_dumps(val, string, error, config=config) if (allocated(error)) return write(io, '(a)', iostat=stat, iomsg=msg) string if (stat /= 0) then call make_error(error, trim(msg)) return end if end subroutine toml_dump_to_unit !> Write string representation of JSON value to a file subroutine toml_dump_to_file(val, filename, error, config) !> TOML value to visit class(toml_value), intent(inout) :: val !> File name to write to character(*), intent(in) :: filename !> Error handling type(toml_error), allocatable, intent(out) :: error !> Configuration for serializer type(toml_ser_config), intent(in), optional :: config integer :: io integer :: stat character(512) :: msg open(file=filename, newunit=io, iostat=stat, iomsg=msg) if (stat /= 0) then call make_error(error, trim(msg)) return end if call toml_dump(val, io, error, config=config) close(unit=io, iostat=stat, iomsg=msg) if (.not.allocated(error) .and. stat /= 0) then call make_error(error, trim(msg)) end if end subroutine toml_dump_to_file !> Constructor to create new serializer instance subroutine new_serializer(self, config) !> Instance of the TOML serializer type(toml_serializer), intent(out) :: self !> Configuration for serializer type(toml_ser_config), intent(in), optional :: config self%output = "" if (present(config)) self%config = config end subroutine new_serializer !> Default constructor for TOML serializer function new_serializer_func(config) result(self) !> Configuration for serializer type(toml_ser_config), intent(in), optional :: config !> Instance of the TOML serializer type(toml_serializer) :: self call new_serializer(self, config) end function new_serializer_func !> Visit a TOML value recursive subroutine visit(self, val) !> Instance of the TOML serializer class(toml_serializer), intent(inout) :: self !> TOML value to visit class(toml_value), intent(inout) :: val select type(val) class is(toml_keyval) call visit_keyval(self, val) class is(toml_array) call visit_array(self, val) class is(toml_table) call visit_table(self, val) end select end subroutine visit !> Visit a TOML key-value pair subroutine visit_keyval(visitor, keyval) !> Instance of the TOML serializer class(toml_serializer), intent(inout) :: visitor !> TOML value to visit type(toml_keyval), intent(inout) :: keyval character(kind=tfc, len=:), allocatable :: key, str type(toml_datetime), pointer :: dval character(:, tfc), pointer :: sval integer(tfi), pointer :: ival real(tfr), pointer :: rval logical, pointer :: lval call keyval%get_key(key) select case(keyval%get_type()) case(toml_type%string) call keyval%get(sval) call toml_escape_string(sval, str) case(toml_type%int) call keyval%get(ival) str = to_string(ival) case(toml_type%float) call keyval%get(rval) str = to_string(rval) case(toml_type%boolean) call keyval%get(lval) if (lval) then str = "true" else str = "false" end if case(toml_type%datetime) call keyval%get(dval) str = to_string(dval) end select if (visitor%inline_array) then visitor%output = visitor%output // " " end if visitor%output = visitor%output // key // " = " // str if (.not.visitor%inline_array) then visitor%output = visitor%output // new_line('a') end if end subroutine visit_keyval !> Visit a TOML array recursive subroutine visit_array(visitor, array) !> Instance of the TOML serializer class(toml_serializer), intent(inout) :: visitor !> TOML value to visit type(toml_array), intent(inout) :: array class(toml_value), pointer :: ptr character(kind=tfc, len=:), allocatable :: key, str type(toml_datetime), pointer :: dval character(:, tfc), pointer :: sval integer(tfi), pointer :: ival real(tfr), pointer :: rval logical, pointer :: lval integer :: i, n if (visitor%inline_array) visitor%output = visitor%output // " [" n = len(array) do i = 1, n call array%get(i, ptr) select type(ptr) class is(toml_keyval) select case(ptr%get_type()) case(toml_type%string) call ptr%get(sval) call toml_escape_string(sval, str) case(toml_type%int) call ptr%get(ival) str = to_string(ival) case(toml_type%float) call ptr%get(rval) str = to_string(rval) case(toml_type%boolean) call ptr%get(lval) if (lval) then str = "true" else str = "false" end if case(toml_type%datetime) call ptr%get(dval) str = to_string(dval) end select visitor%output = visitor%output // " " // str if (i /= n) visitor%output = visitor%output // "," class is(toml_array) call ptr%accept(visitor) if (i /= n) visitor%output = visitor%output // "," class is(toml_table) if (visitor%inline_array) then visitor%output = visitor%output // " {" call ptr%accept(visitor) visitor%output = visitor%output // " }" if (i /= n) visitor%output = visitor%output // "," else visitor%array_of_tables = .true. if (size(visitor%stack, 1) <= visitor%top) call resize(visitor%stack) visitor%top = visitor%top + 1 call array%get_key(key) visitor%stack(visitor%top)%key = key call ptr%accept(visitor) deallocate(visitor%stack(visitor%top)%key) visitor%top = visitor%top - 1 end if end select end do if (visitor%inline_array) visitor%output = visitor%output // " ]" end subroutine visit_array !> Visit a TOML table recursive subroutine visit_table(visitor, table) !> Instance of the TOML serializer class(toml_serializer), intent(inout) :: visitor !> TOML table to visit type(toml_table), intent(inout) :: table class(toml_value), pointer :: ptr type(toml_key), allocatable :: list(:) logical, allocatable :: defer(:) character(kind=tfc, len=:), allocatable :: key integer :: i, n call table%get_keys(list) n = size(list, 1) allocate(defer(n)) if (.not.allocated(visitor%stack)) then call resize(visitor%stack) else if (.not.(visitor%inline_array .or. table%implicit)) then visitor%output = visitor%output // "[" if (visitor%array_of_tables) visitor%output = visitor%output // "[" do i = 1, visitor%top-1 visitor%output = visitor%output // visitor%stack(i)%key // "." end do visitor%output = visitor%output // visitor%stack(visitor%top)%key visitor%output = visitor%output // "]" if (visitor%array_of_tables) visitor%output = visitor%output // "]" visitor%output = visitor%output // new_line('a') visitor%array_of_tables = .false. end if end if do i = 1, n defer(i) = .false. call table%get(list(i)%key, ptr) select type(ptr) class is(toml_keyval) call ptr%accept(visitor) if (visitor%inline_array) then if (i /= n) visitor%output = visitor%output // "," end if class is(toml_array) if (visitor%inline_array) then call ptr%get_key(key) visitor%output = visitor%output // " " // key // " =" call ptr%accept(visitor) if (i /= n) visitor%output = visitor%output // "," else if (is_array_of_tables(ptr)) then ! Array of tables open a new section ! -> cannot serialize them before all key-value pairs are done defer(i) = .true. else visitor%inline_array = .true. call ptr%get_key(key) visitor%output = visitor%output // key // " =" call ptr%accept(visitor) visitor%inline_array = .false. visitor%output = visitor%output // new_line('a') end if end if class is(toml_table) ! Subtables open a new section ! -> cannot serialize them before all key-value pairs are done defer(i) = .true. end select end do do i = 1, n if (defer(i)) then call table%get(list(i)%key, ptr) select type(ptr) class is(toml_keyval) call ptr%accept(visitor) if (visitor%inline_array) then if (i /= n) visitor%output = visitor%output // "," end if class is(toml_array) if (visitor%inline_array) then call ptr%get_key(key) visitor%output = visitor%output // " " // key // " =" call ptr%accept(visitor) if (i /= n) visitor%output = visitor%output // "," else if (is_array_of_tables(ptr)) then call ptr%accept(visitor) else visitor%inline_array = .true. call ptr%get_key(key) visitor%output = visitor%output // key // " =" call ptr%accept(visitor) visitor%inline_array = .false. visitor%output = visitor%output // new_line('a') end if end if class is(toml_table) if (size(visitor%stack, 1) <= visitor%top) call resize(visitor%stack) visitor%top = visitor%top + 1 call ptr%get_key(key) visitor%stack(visitor%top)%key = key call ptr%accept(visitor) deallocate(visitor%stack(visitor%top)%key) visitor%top = visitor%top - 1 end select end if end do if (.not.visitor%inline_array .and. visitor%top == 0) then deallocate(visitor%stack) end if end subroutine visit_table !> Change size of the stack subroutine resize(stack, n) !> Stack of keys to be resized type(toml_key), allocatable, intent(inout) :: stack(:) !> New size of the stack integer, intent(in), optional :: n type(toml_key), allocatable :: tmp(:) integer :: m if (present(n)) then m = n else if (allocated(stack)) then m = size(stack) m = m + m/2 + 1 else m = initial_size end if end if if (allocated(stack)) then call move_alloc(stack, tmp) allocate(stack(m)) m = min(size(tmp), m) stack(:m) = tmp(:m) deallocate(tmp) else allocate(stack(m)) end if end subroutine resize end module tomlf_ser fortran-toml-0.5.0/src/tomlf/constants.f900000664000175000017500000001232115201541453020567 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tomlf_constants use, intrinsic :: iso_fortran_env, only : output_unit implicit none private !> Single precision real numbers integer, public, parameter :: tf_sp = selected_real_kind(6) !> Double precision real numbers integer, public, parameter :: tf_dp = selected_real_kind(15) !> Char length for integers integer, public, parameter :: tf_i1 = selected_int_kind(2) !> Short length for integers integer, public, parameter :: tf_i2 = selected_int_kind(4) !> Length of default integers integer, public, parameter :: tf_i4 = selected_int_kind(9) !> Long length for integers integer, public, parameter :: tf_i8 = selected_int_kind(18) !> Default character kind integer, public, parameter :: tfc = selected_char_kind('DEFAULT') !> Default float precision, IEEE 754 binary64 values expected integer, public, parameter :: tfr = tf_dp !> Default integer precision, 64 bit (signed long) range expected integer, public, parameter :: tfi = tf_i8 !> Default output channel integer, public, parameter :: tfout = output_unit !> Possible escape characters in TOML type :: enum_escape !> Backslash is used to escape other characters character(kind=tfc, len=1) :: backslash = tfc_'\' !> Double quotes signal strings with escape characters enabled character(kind=tfc, len=1) :: dquote = tfc_'"' !> Single quotes signal strings without escape characters enabled character(kind=tfc, len=1) :: squote = tfc_'''' !> Newline character character(kind=tfc, len=1) :: newline = achar(10, kind=tfc) !> Formfeed character is allowed in strings character(kind=tfc, len=1) :: formfeed = achar(12, kind=tfc) !> Carriage return is allowed as part of the newline and in strings character(kind=tfc, len=1) :: carriage_return = achar(13, kind=tfc) !> Backspace is allowed in strings character(kind=tfc, len=1) :: bspace = achar(8, kind=tfc) !> Tabulators are allowed as whitespace and in strings character(kind=tfc, len=1) :: tabulator = achar(9, kind=tfc) end type enum_escape !> Actual enumerator with TOML escape characters type(enum_escape), public, parameter :: toml_escape = enum_escape() !> Possible kinds of TOML values in key-value pairs type :: enum_type !> Invalid type integer :: invalid = 100 !> String type integer :: string = 101 !> Boolean type integer :: boolean = 102 !> Integer type integer :: int = 103 !> Float type integer :: float = 104 !> Datetime type integer :: datetime = 105 end type enum_type !> Actual enumerator with TOML value types type(enum_type), public, parameter :: toml_type = enum_type() !> Single quotes denote literal strings character(kind=tfc, len=*), public, parameter :: TOML_SQUOTE = "'" !> Double quotes denote strings (with escape character possible) character(kind=tfc, len=*), public, parameter :: TOML_DQUOTE = '"' character(kind=tfc, len=*), public, parameter :: TOML_NEWLINE = new_line('a') ! \n character(kind=tfc, len=*), public, parameter :: TOML_TABULATOR = achar(9) ! \t character(kind=tfc, len=*), public, parameter :: TOML_FORMFEED = achar(12) ! \f character(kind=tfc, len=*), public, parameter :: TOML_CARRIAGE_RETURN = achar(13) ! \r character(kind=tfc, len=*), public, parameter :: TOML_BACKSPACE = achar(8) ! \b character(kind=tfc, len=*), public, parameter :: TOML_ESC = achar(27) ! \e character(kind=tfc, len=*), public, parameter :: TOML_LOWERCASE = & & 'abcdefghijklmnopqrstuvwxyz' character(kind=tfc, len=*), public, parameter :: TOML_UPPERCASE = & & 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' character(kind=tfc, len=*), public, parameter :: TOML_LETTERS = & & TOML_LOWERCASE//TOML_UPPERCASE !> Whitespace in TOML are blanks and tabs. character(kind=tfc, len=*), public, parameter :: TOML_WHITESPACE = & & ' '//toml_escape%tabulator character(kind=tfc, len=*), public, parameter :: TOML_DIGITS = '0123456789' character(kind=tfc, len=*), public, parameter :: TOML_BINDIGITS = & & '01' character(kind=tfc, len=*), public, parameter :: TOML_OCTDIGITS = & & '01234567' character(kind=tfc, len=*), public, parameter :: TOML_HEXDIGITS = & & '0123456789ABCDEFabcdef' character(kind=tfc, len=*), public, parameter :: TOML_TIMESTAMP = & & TOML_DIGITS//'.:+-T Zz' !> Allowed characters in TOML bare keys. character(kind=tfc, len=*), public, parameter :: TOML_BAREKEY = & & TOML_LETTERS//TOML_DIGITS//'_-' character(kind=tfc, len=*), public, parameter :: TOML_LITERALS = & & TOML_LETTERS//TOML_DIGITS//'_-+.' end module tomlf_constants fortran-toml-0.5.0/src/tomlf/build/0000775000175000017500000000000015201541453017333 5ustar alastairalastairfortran-toml-0.5.0/src/tomlf/build/table.f900000664000175000017500000011505315201541453020747 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Functions to build TOML tables !> !> The build module defines a high level interface to work with TOML tables !> and construct them in a convenient way. !> !> The getter functions allow to both retrieve and set values, to easily !> support default values when reading from a TOML data structure. !> Using the getter function with a default value specified will request !> the respective setter function to add it to the table if it was not !> found in the first place. !> !> This allows to build a TOML table using only the getter functions, which !> represents the finally read values for the applications. !> !> Note that neither setter nor getter functions can overwrite existing !> TOML values for safety reasons, request the deletion on the respective !> key from the TOML table and than set it. The deletion of a subtable or !> array will recursively destroy the contained data nodes. module tomlf_build_table use tomlf_build_keyval, only : get_value, set_value use tomlf_constants, only : tfc, tfi, tfr, tf_i1, tf_i2, tf_i4, tf_i8, & & tf_sp, tf_dp use tomlf_datetime, only : toml_datetime use tomlf_error, only : toml_stat use tomlf_type, only : toml_value, toml_table, toml_array, toml_keyval, & & new_table, new_array, new_keyval, add_table, add_array, add_keyval, & & toml_key, cast_to_table, cast_to_array, cast_to_keyval, initialized, & & len implicit none private public :: get_value, set_value !> Setter functions to manipulate TOML tables interface set_value module procedure :: set_child_value_float_sp module procedure :: set_child_value_float_dp module procedure :: set_child_value_integer_i1 module procedure :: set_child_value_integer_i2 module procedure :: set_child_value_integer_i4 module procedure :: set_child_value_integer_i8 module procedure :: set_child_value_bool module procedure :: set_child_value_datetime module procedure :: set_child_value_string module procedure :: set_key_value_float_sp module procedure :: set_key_value_float_dp module procedure :: set_key_value_integer_i1 module procedure :: set_key_value_integer_i2 module procedure :: set_key_value_integer_i4 module procedure :: set_key_value_integer_i8 module procedure :: set_key_value_bool module procedure :: set_key_value_datetime module procedure :: set_key_value_string end interface set_value !> Getter functions to manipulate TOML tables interface get_value module procedure :: get_child_table module procedure :: get_child_array module procedure :: get_child_keyval module procedure :: get_child_value_float_sp module procedure :: get_child_value_float_dp module procedure :: get_child_value_integer_i1 module procedure :: get_child_value_integer_i2 module procedure :: get_child_value_integer_i4 module procedure :: get_child_value_integer_i8 module procedure :: get_child_value_bool module procedure :: get_child_value_datetime module procedure :: get_child_value_string module procedure :: get_key_table module procedure :: get_key_array module procedure :: get_key_keyval module procedure :: get_key_value_float_sp module procedure :: get_key_value_float_dp module procedure :: get_key_value_integer_i1 module procedure :: get_key_value_integer_i2 module procedure :: get_key_value_integer_i4 module procedure :: get_key_value_integer_i8 module procedure :: get_key_value_bool module procedure :: get_key_value_datetime module procedure :: get_key_value_string end interface get_value contains subroutine get_key_table(table, key, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Pointer to child table type(toml_table), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, ptr, requested, stat, origin) end subroutine get_key_table subroutine get_key_array(table, key, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Pointer to child array type(toml_array), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, ptr, requested, stat, origin) end subroutine get_key_array subroutine get_key_keyval(table, key, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Pointer to child value type(toml_keyval), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, ptr, requested, stat, origin) end subroutine get_key_keyval !> Retrieve TOML value as single precision float (might lose accuracy) subroutine get_key_value_float_sp(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Real value real(tf_sp), intent(out) :: val !> Default real value real(tf_sp), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_float_sp !> Retrieve TOML value as double precision float subroutine get_key_value_float_dp(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Real value real(tf_dp), intent(out) :: val !> Default real value real(tf_dp), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_float_dp !> Retrieve TOML value as one byte integer (might loose precision) subroutine get_key_value_integer_i1(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Integer value integer(tf_i1), intent(out) :: val !> Default integer value integer(tf_i1), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_integer_i1 !> Retrieve TOML value as two byte integer (might loose precision) subroutine get_key_value_integer_i2(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Integer value integer(tf_i2), intent(out) :: val !> Default integer value integer(tf_i2), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_integer_i2 !> Retrieve TOML value as four byte integer (might loose precision) subroutine get_key_value_integer_i4(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Integer value integer(tf_i4), intent(out) :: val !> Default integer value integer(tf_i4), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_integer_i4 !> Retrieve TOML value as eight byte integer subroutine get_key_value_integer_i8(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Integer value integer(tf_i8), intent(out) :: val !> Default integer value integer(tf_i8), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_integer_i8 !> Retrieve TOML value as logical subroutine get_key_value_bool(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Boolean value logical, intent(out) :: val !> Default boolean value logical, intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_bool !> Retrieve TOML value as datetime subroutine get_key_value_datetime(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Datetime value type(toml_datetime), intent(out) :: val !> Default datetime value type(toml_datetime), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_datetime !> Retrieve TOML value as deferred-length character subroutine get_key_value_string(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> String value character(kind=tfc, len=:), allocatable, intent(out) :: val !> Default string value character(kind=tfc, len=*), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_string !> Set TOML value to single precision float subroutine set_key_value_float_sp(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Real value real(tf_sp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_float_sp !> Set TOML value to double precision float subroutine set_key_value_float_dp(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Real value real(tf_dp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_float_dp !> Set TOML value to one byte integer subroutine set_key_value_integer_i1(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Integer value integer(tf_i1), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_integer_i1 !> Set TOML value to two byte integer subroutine set_key_value_integer_i2(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Integer value integer(tf_i2), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_integer_i2 !> Set TOML value to four byte integer subroutine set_key_value_integer_i4(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Integer value integer(tf_i4), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_integer_i4 !> Set TOML value to eight byte integer subroutine set_key_value_integer_i8(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Integer value integer(tf_i8), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_integer_i8 !> Set TOML value to logical subroutine set_key_value_bool(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Boolean value logical, intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_bool !> Set TOML value to datetime subroutine set_key_value_datetime(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Datetime value type(toml_datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_datetime !> Set TOML value to deferred-length character subroutine set_key_value_string(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> String value character(kind=tfc, len=*), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_string subroutine get_child_table(table, key, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Pointer to child table type(toml_table), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin class(toml_value), pointer :: tmp logical :: is_requested if (.not.initialized(table)) call new_table(table) if (present(requested)) then is_requested = requested else is_requested = .true. end if nullify(ptr) call table%get(key, tmp) if (associated(tmp)) then ptr => cast_to_table(tmp) if (present(stat)) then if (associated(ptr)) then stat = toml_stat%success else stat = toml_stat%type_mismatch end if end if if (present(origin)) origin = tmp%origin else if (is_requested) then call add_table(table, key, ptr, stat) else if (present(stat)) stat = toml_stat%success end if if (present(origin)) origin = table%origin end if end subroutine get_child_table subroutine get_child_array(table, key, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Pointer to child array type(toml_array), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin class(toml_value), pointer :: tmp logical :: is_requested if (.not.initialized(table)) call new_table(table) if (present(requested)) then is_requested = requested else is_requested = .true. end if nullify(ptr) call table%get(key, tmp) if (associated(tmp)) then ptr => cast_to_array(tmp) if (present(stat)) then if (associated(ptr)) then stat = toml_stat%success else stat = toml_stat%type_mismatch end if end if if (present(origin)) origin = tmp%origin else if (is_requested) then call add_array(table, key, ptr, stat) else if (present(stat)) stat = toml_stat%success end if if (present(origin)) origin = table%origin end if end subroutine get_child_array subroutine get_child_keyval(table, key, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Pointer to child value type(toml_keyval), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin class(toml_value), pointer :: tmp logical :: is_requested if (.not.initialized(table)) call new_table(table) if (present(requested)) then is_requested = requested else is_requested = .true. end if nullify(ptr) call table%get(key, tmp) if (associated(tmp)) then ptr => cast_to_keyval(tmp) if (present(stat)) then if (associated(ptr)) then stat = toml_stat%success else stat = toml_stat%type_mismatch end if end if if (present(origin)) origin = tmp%origin else if (is_requested) then call add_keyval(table, key, ptr, stat) else if (present(stat)) stat = toml_stat%success end if if (present(origin)) origin = table%origin end if end subroutine get_child_keyval !> Retrieve TOML value as single precision float (might lose accuracy) subroutine get_child_value_float_sp(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Real value real(tf_sp), intent(out) :: val !> Default real value real(tf_sp), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_float_sp !> Retrieve TOML value as double precision float subroutine get_child_value_float_dp(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Real value real(tf_dp), intent(out) :: val !> Default real value real(tf_dp), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_float_dp !> Retrieve TOML value as one byte integer (might loose precision) subroutine get_child_value_integer_i1(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Integer value integer(tf_i1), intent(out) :: val !> Default integer value integer(tf_i1), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_integer_i1 !> Retrieve TOML value as two byte integer (might loose precision) subroutine get_child_value_integer_i2(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Integer value integer(tf_i2), intent(out) :: val !> Default integer value integer(tf_i2), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_integer_i2 !> Retrieve TOML value as four byte integer (might loose precision) subroutine get_child_value_integer_i4(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Integer value integer(tf_i4), intent(out) :: val !> Default integer value integer(tf_i4), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_integer_i4 !> Retrieve TOML value as eight byte integer subroutine get_child_value_integer_i8(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Integer value integer(tf_i8), intent(out) :: val !> Default integer value integer(tf_i8), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_integer_i8 !> Retrieve TOML value as logical subroutine get_child_value_bool(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Boolean value logical, intent(out) :: val !> Default boolean value logical, intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_bool !> Retrieve TOML value as datetime subroutine get_child_value_datetime(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Datetime value type(toml_datetime), intent(out) :: val !> Default datetime value type(toml_datetime), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_datetime !> Retrieve TOML value as deferred-length character subroutine get_child_value_string(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> String value character(kind=tfc, len=:), allocatable, intent(out) :: val !> Default string value character(kind=tfc, len=*), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if else if (.not.present(default)) then if (present(stat)) stat = merge(toml_stat%missing_key, stat, stat == toml_stat%success) end if end subroutine get_child_value_string !> Set TOML value to single precision float subroutine set_child_value_float_sp(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Real value real(tf_sp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_float_sp !> Set TOML value to double precision float subroutine set_child_value_float_dp(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Real value real(tf_dp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_float_dp !> Set TOML value to one byte integer subroutine set_child_value_integer_i1(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Integer value integer(tf_i1), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_integer_i1 !> Set TOML value to two byte integer subroutine set_child_value_integer_i2(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Integer value integer(tf_i2), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_integer_i2 !> Set TOML value to four byte integer subroutine set_child_value_integer_i4(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Integer value integer(tf_i4), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_integer_i4 !> Set TOML value to eight byte integer subroutine set_child_value_integer_i8(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Integer value integer(tf_i8), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_integer_i8 !> Set TOML value to logical subroutine set_child_value_bool(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Boolean value logical, intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_bool !> Set TOML value to datetime subroutine set_child_value_datetime(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> Datetime value type(toml_datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_datetime !> Set TOML value to deferred-length character subroutine set_child_value_string(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(kind=tfc, len=*), intent(in) :: key !> String value character(kind=tfc, len=*), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_string end module tomlf_build_table fortran-toml-0.5.0/src/tomlf/build/path.f900000664000175000017500000005344315201541453020620 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Support for retrieving and setting values using a key path. module tomlf_build_path use tomlf_build_table, only : get_value, set_value use tomlf_constants, only : tfc, tfi, tfr, tf_i1, tf_i2, tf_i4, tf_i8, & & tf_sp, tf_dp use tomlf_datetime, only : toml_datetime use tomlf_error, only : toml_stat use tomlf_type, only : toml_table, toml_array, toml_keyval, toml_key implicit none private public :: toml_path, get_value, set_value !> Setter functions to manipulate TOML tables interface set_value module procedure :: set_path_value_float_sp module procedure :: set_path_value_float_dp module procedure :: set_path_value_integer_i1 module procedure :: set_path_value_integer_i2 module procedure :: set_path_value_integer_i4 module procedure :: set_path_value_integer_i8 module procedure :: set_path_value_bool module procedure :: set_path_value_datetime module procedure :: set_path_value_string end interface set_value !> Getter functions to manipulate TOML tables interface get_value module procedure :: get_path_table module procedure :: get_path_array module procedure :: get_path_keyval module procedure :: get_path_value_float_sp module procedure :: get_path_value_float_dp module procedure :: get_path_value_integer_i1 module procedure :: get_path_value_integer_i2 module procedure :: get_path_value_integer_i4 module procedure :: get_path_value_integer_i8 module procedure :: get_path_value_bool module procedure :: get_path_value_datetime module procedure :: get_path_value_string end interface get_value !> Wrapper for storing key paths type :: toml_path !> Path components type(toml_key), allocatable :: path(:) end type toml_path !> Convenience constructors for building key paths from strings instead of keys interface toml_path module procedure :: new_path2 module procedure :: new_path3 module procedure :: new_path4 end interface toml_path contains !> Create a new path with two components pure function new_path2(key1, key2) result(path) !> First key to retrieve character(*), intent(in) :: key1 !> Second key to retrieve character(*), intent(in) :: key2 !> New path type(toml_path) :: path allocate(path%path(2)) path%path(:) = [toml_key(key1), toml_key(key2)] end function new_path2 !> Create a new path with three components pure function new_path3(key1, key2, key3) result(path) !> First key to retrieve character(*, tfc), intent(in) :: key1 !> Second key to retrieve character(*, tfc), intent(in) :: key2 !> Third key to retrieve character(*, tfc), intent(in) :: key3 !> New path type(toml_path) :: path allocate(path%path(3)) path%path(:) = [toml_key(key1), toml_key(key2), toml_key(key3)] end function new_path3 !> Create a new path with three components pure function new_path4(key1, key2, key3, key4) result(path) !> First key to retrieve character(*, tfc), intent(in) :: key1 !> Second key to retrieve character(*, tfc), intent(in) :: key2 !> Third key to retrieve character(*, tfc), intent(in) :: key3 !> Forth key to retrieve character(*, tfc), intent(in) :: key4 !> New path type(toml_path) :: path allocate(path%path(4)) path%path(:) = [toml_key(key1), toml_key(key2), toml_key(key3), toml_key(key4)] end function new_path4 subroutine get_path_table(table, path, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout), target :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Pointer to child table type(toml_table), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child logical :: is_requested is_requested = .true. if (present(requested)) is_requested = requested nullify(ptr) call walk_path(table, path, child, is_requested, stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), ptr, is_requested, stat, origin) else if (.not.is_requested .and. present(stat)) stat = toml_stat%success end if end subroutine get_path_table subroutine get_path_array(table, path, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Pointer to child array type(toml_array), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child logical :: is_requested is_requested = .true. if (present(requested)) is_requested = requested nullify(ptr) call walk_path(table, path, child, is_requested, stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), ptr, is_requested, stat, origin) else if (.not.is_requested .and. present(stat)) stat = toml_stat%success end if end subroutine get_path_array subroutine get_path_keyval(table, path, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Pointer to child value type(toml_keyval), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child logical :: is_requested is_requested = .true. if (present(requested)) is_requested = requested nullify(ptr) call walk_path(table, path, child, is_requested, stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), ptr, is_requested, stat, origin) else if (.not.is_requested .and. present(stat)) stat = toml_stat%success end if end subroutine get_path_keyval !> Retrieve TOML value as single precision float (might lose accuracy) subroutine get_path_value_float_sp(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Real value real(tf_sp), intent(out) :: val !> Default real value real(tf_sp), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_float_sp !> Retrieve TOML value as double precision float subroutine get_path_value_float_dp(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Real value real(tf_dp), intent(out) :: val !> Default real value real(tf_dp), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_float_dp !> Retrieve TOML value as one byte integer (might loose precision) subroutine get_path_value_integer_i1(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Integer value integer(tf_i1), intent(out) :: val !> Default integer value integer(tf_i1), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_integer_i1 !> Retrieve TOML value as two byte integer (might loose precision) subroutine get_path_value_integer_i2(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Integer value integer(tf_i2), intent(out) :: val !> Default integer value integer(tf_i2), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_integer_i2 !> Retrieve TOML value as four byte integer (might loose precision) subroutine get_path_value_integer_i4(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Integer value integer(tf_i4), intent(out) :: val !> Default integer value integer(tf_i4), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_integer_i4 !> Retrieve TOML value as eight byte integer subroutine get_path_value_integer_i8(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Integer value integer(tf_i8), intent(out) :: val !> Default integer value integer(tf_i8), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_integer_i8 !> Retrieve TOML value as logical subroutine get_path_value_bool(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Boolean value logical, intent(out) :: val !> Default boolean value logical, intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_bool !> Retrieve TOML value as datetime subroutine get_path_value_datetime(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Datetime value type(toml_datetime), intent(out) :: val !> Default datetime value type(toml_datetime), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_datetime !> Retrieve TOML value as deferred-length character subroutine get_path_value_string(table, path, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> String value character(kind=tfc, len=:), allocatable, intent(out) :: val !> Default string value character(kind=tfc, len=*), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, present(default), stat, origin) if (associated(child)) then call get_value(child, path%path(size(path%path)), val, default, stat, origin) end if end subroutine get_path_value_string !> Set TOML value to single precision float subroutine set_path_value_float_sp(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Real value real(tf_sp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_float_sp !> Set TOML value to double precision float subroutine set_path_value_float_dp(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Real value real(tf_dp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_float_dp !> Set TOML value to one byte integer subroutine set_path_value_integer_i1(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Integer value integer(tf_i1), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_integer_i1 !> Set TOML value to two byte integer subroutine set_path_value_integer_i2(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Integer value integer(tf_i2), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_integer_i2 !> Set TOML value to four byte integer subroutine set_path_value_integer_i4(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Integer value integer(tf_i4), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_integer_i4 !> Set TOML value to eight byte integer subroutine set_path_value_integer_i8(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Integer value integer(tf_i8), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_integer_i8 !> Set TOML value to logical subroutine set_path_value_bool(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Boolean value logical, intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_bool !> Set TOML value to datetime subroutine set_path_value_datetime(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Datetime value type(toml_datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_datetime !> Set TOML value to deferred-length character subroutine set_path_value_string(table, path, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> String value character(kind=tfc, len=*), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_table), pointer :: child call walk_path(table, path, child, .true., stat, origin) if (associated(child)) then call set_value(child, path%path(size(path%path)), val, stat, origin) end if end subroutine set_path_value_string subroutine walk_path(table, path, ptr, requested, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout), target :: table !> Path in this TOML table type(toml_path), intent(in) :: path !> Pointer to child table type(toml_table), pointer, intent(out) :: ptr !> Child value must be present logical, intent(in), optional :: requested !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it type(toml_table), pointer :: current, next nullify(ptr) if (.not.allocated(path%path)) then if (present(stat)) stat = toml_stat%fatal if (present(origin)) origin = table%origin return end if current => table do it = 1, size(path%path) - 1 call get_value(current, path%path(it)%key, next, requested, stat, origin) if (.not.associated(next)) then if (present(stat)) stat = toml_stat%fatal if (present(origin)) origin = current%origin return end if current => next end do ptr => current end subroutine walk_path end module tomlf_build_path fortran-toml-0.5.0/src/tomlf/build/meson.build0000664000175000017500000000117415201541453021500 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. srcs += files( 'array.f90', 'keyval.f90', 'merge.f90', 'path.f90', 'table.f90', ) fortran-toml-0.5.0/src/tomlf/build/keyval.f900000664000175000017500000003600315201541453021150 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Functions to build a TOML values !> !> The build module defines an interface to work with TOML values instead !> of accessing the raw value directly. Both setter and getter routines defined !> here are rarely needed in any user context, but serve as a basic building !> block to define uniform access methods for TOML tables and arrays. module tomlf_build_keyval use tomlf_constants, only : tfc, tfi, tfr, tf_i1, tf_i2, tf_i4, tf_i8, & & tf_sp, tf_dp, TOML_NEWLINE use tomlf_datetime, only : toml_datetime use tomlf_error, only : toml_stat use tomlf_type, only : toml_value, toml_table, toml_array, toml_keyval, & & new_table, new_array, new_keyval, add_table, add_array, add_keyval, len use tomlf_utils, only : toml_escape_string, to_string implicit none private public :: get_value, set_value !> Setter functions to manipulate TOML values interface set_value module procedure :: set_value_float_sp module procedure :: set_value_float_dp module procedure :: set_value_integer_i1 module procedure :: set_value_integer_i2 module procedure :: set_value_integer_i4 module procedure :: set_value_integer_i8 module procedure :: set_value_bool module procedure :: set_value_datetime module procedure :: set_value_string end interface set_value !> Getter functions to manipulate TOML values interface get_value module procedure :: get_value_float_sp module procedure :: get_value_float_dp module procedure :: get_value_integer_i1 module procedure :: get_value_integer_i2 module procedure :: get_value_integer_i4 module procedure :: get_value_integer_i8 module procedure :: get_value_bool module procedure :: get_value_datetime module procedure :: get_value_string end interface get_value !> Length for the static character variables integer, parameter :: buffersize = 128 contains !> Retrieve TOML value as single precision float (might lose accuracy) subroutine get_value_float_sp(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Real value real(tf_sp), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info real(tfr), pointer :: dummy integer(tfi), pointer :: idummy call self%get(dummy) if (associated(dummy)) then val = real(dummy, tf_sp) info = toml_stat%success else call self%get(idummy) if (associated(idummy)) then val = real(idummy, tf_sp) if (nint(val, tfi) == idummy) then info = toml_stat%success else info = toml_stat%conversion_error end if else info = toml_stat%type_mismatch end if end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_float_sp !> Retrieve TOML value as double precision float subroutine get_value_float_dp(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Real value real(tf_dp), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info real(tfr), pointer :: dummy integer(tfi), pointer :: idummy call self%get(dummy) if (associated(dummy)) then val = real(dummy, tf_dp) info = toml_stat%success else call self%get(idummy) if (associated(idummy)) then val = real(idummy, tf_dp) if (nint(val, tfi) == idummy) then info = toml_stat%success else info = toml_stat%conversion_error end if else info = toml_stat%type_mismatch end if end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_float_dp !> Retrieve TOML value as one byte integer (might loose precision) subroutine get_value_integer_i1(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Integer value integer(tf_i1), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info integer(tfi), pointer :: dummy call self%get(dummy) if (associated(dummy)) then val = int(dummy, tf_i1) if (dummy <= huge(val) .and. dummy >= -huge(val)-1) then info = toml_stat%success else info = toml_stat%conversion_error end if else info = toml_stat%type_mismatch end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_integer_i1 !> Retrieve TOML value as two byte integer (might loose precision) subroutine get_value_integer_i2(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Integer value integer(tf_i2), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info integer(tfi), pointer :: dummy call self%get(dummy) if (associated(dummy)) then val = int(dummy, tf_i2) if (dummy <= huge(val) .and. dummy >= -huge(val)-1) then info = toml_stat%success else info = toml_stat%conversion_error end if else info = toml_stat%type_mismatch end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_integer_i2 !> Retrieve TOML value as four byte integer (might loose precision) subroutine get_value_integer_i4(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Integer value integer(tf_i4), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info integer(tfi), pointer :: dummy call self%get(dummy) if (associated(dummy)) then val = int(dummy, tf_i4) if (dummy <= huge(val) .and. dummy >= -huge(val)-1) then info = toml_stat%success else info = toml_stat%conversion_error end if else info = toml_stat%type_mismatch end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_integer_i4 !> Retrieve TOML value as eight byte integer subroutine get_value_integer_i8(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Integer value integer(tf_i8), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info integer(tfi), pointer :: dummy call self%get(dummy) if (associated(dummy)) then val = int(dummy, tf_i8) info = toml_stat%success else info = toml_stat%type_mismatch end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_integer_i8 !> Retrieve TOML value as logical subroutine get_value_bool(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Boolean value logical, intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info logical, pointer :: dummy call self%get(dummy) if (associated(dummy)) then val = dummy info = toml_stat%success else info = toml_stat%type_mismatch end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_bool !> Retrieve TOML value as datetime subroutine get_value_datetime(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Datetime value type(toml_datetime), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info type(toml_datetime), pointer :: dummy call self%get(dummy) if (associated(dummy)) then val = dummy info = toml_stat%success else info = toml_stat%type_mismatch end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_datetime !> Retrieve TOML value as deferred-length character subroutine get_value_string(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> String value character(kind=tfc, len=:), allocatable, intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: info character(:, tfc), pointer :: dummy call self%get(dummy) if (associated(dummy)) then val = dummy info = toml_stat%success else info = toml_stat%type_mismatch end if if (present(stat)) stat = info if (present(origin)) origin = self%origin_value end subroutine get_value_string !> Set TOML value to single precision float subroutine set_value_float_sp(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Real value real(tf_sp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(real(val, tfr)) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_float_sp !> Set TOML value to double precision float subroutine set_value_float_dp(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Real value real(tf_dp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(real(val, tfr)) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_float_dp !> Set TOML value to one byte integer subroutine set_value_integer_i1(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Integer value integer(tf_i1), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(int(val, tfi)) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_integer_i1 !> Set TOML value to two byte integer subroutine set_value_integer_i2(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Integer value integer(tf_i2), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(int(val, tfi)) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_integer_i2 !> Set TOML value to four byte integer subroutine set_value_integer_i4(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Integer value integer(tf_i4), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(int(val, tfi)) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_integer_i4 !> Set TOML value to eight byte integer subroutine set_value_integer_i8(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Integer value integer(tf_i8), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(int(val, tfi)) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_integer_i8 !> Set TOML value to logical subroutine set_value_bool(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Boolean value logical, intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(val) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_bool !> Set TOML value to datetime subroutine set_value_datetime(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Datetime value type(toml_datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(val) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_datetime !> Set TOML value to deferred-length character subroutine set_value_string(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> String value character(kind=tfc, len=*), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call self%set(val) if (present(stat)) stat = toml_stat%success self%origin_value = 0 if (present(origin)) origin = self%origin end subroutine set_value_string end module tomlf_build_keyval fortran-toml-0.5.0/src/tomlf/build/merge.f900000664000175000017500000001355515201541453020763 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Merge TOML data structures, the merge policy can be adjusted. !> !> Note that the context information cannot be preserved. module tomlf_build_merge use tomlf_constants, only : tfc use tomlf_type, only : toml_table, toml_array, toml_keyval, toml_value, & & toml_key, cast_to_keyval, len implicit none private public :: merge_table, merge_array, merge_policy, toml_merge_config !> Possible merge policies type :: enum_policy !> Overwrite existing values integer :: overwrite = 1 !> Preserve existing values integer :: preserve = 2 !> Append to existing values integer :: append = 3 end type enum_policy !> Actual enumerator for merging data structures type(enum_policy), parameter :: merge_policy = enum_policy() !> Configuration for merging data structures type :: toml_merge_config !> Policy for merging tables integer :: table = merge_policy%append !> Policy for merging arrays integer :: array = merge_policy%preserve !> Policy for merging values integer :: keyval = merge_policy%preserve end type toml_merge_config !> Constructor for merge configuration interface toml_merge_config module procedure :: new_merge_config end interface toml_merge_config contains !> Create a new merge configuration pure function new_merge_config(table, array, keyval) result(config) !> Policy for merging tables character(*), intent(in), optional :: table !> Policy for merging arrays character(*), intent(in), optional :: array !> Policy for merging values character(*), intent(in), optional :: keyval !> Merge policy type(toml_merge_config) :: config if (present(table)) call set_enum(config%table, table) if (present(array)) call set_enum(config%array, array) if (present(keyval)) call set_enum(config%keyval, keyval) contains pure subroutine set_enum(enum, str) character(*), intent(in) :: str integer, intent(inout) :: enum select case(str) case("append") enum = merge_policy%append case("overwrite") enum = merge_policy%overwrite case("preserve") enum = merge_policy%preserve end select end subroutine set_enum end function new_merge_config !> Merge TOML tables by appending their values recursive subroutine merge_table(lhs, rhs, config) !> Instance of table to merge into class(toml_table), intent(inout) :: lhs !> Instance of table to be merged class(toml_table), intent(inout) :: rhs !> Merge policy type(toml_merge_config), intent(in), optional :: config type(toml_merge_config) :: policy type(toml_key), allocatable :: list(:) class(toml_value), pointer :: ptr1, ptr2 class(toml_keyval), pointer :: kv class(toml_value), allocatable :: tmp logical :: has_key integer :: i, n, stat policy = toml_merge_config() if (present(config)) policy = config call rhs%get_keys(list) n = size(list, 1) do i = 1, n if (allocated(tmp)) deallocate(tmp) call rhs%get(list(i)%key, ptr1) has_key = lhs%has_key(list(i)%key) select type(ptr1) class is(toml_keyval) if (has_key .and. policy%keyval == merge_policy%overwrite) then call lhs%delete(list(i)%key) has_key = .false. end if if (.not.has_key) then allocate(tmp, source=ptr1) kv => cast_to_keyval(tmp) kv%origin_value = 0 kv%origin = 0 call lhs%push_back(tmp, stat) end if class is(toml_array) if (has_key .and. policy%array == merge_policy%overwrite) then call lhs%delete(list(i)%key) has_key = .false. end if if (has_key .and. policy%array == merge_policy%append) then call lhs%get(list(i)%key, ptr2) select type(ptr2) class is(toml_array) call merge_array(ptr2, ptr1) end select end if if (.not.has_key) then allocate(tmp, source=ptr1) tmp%origin = 0 call lhs%push_back(tmp, stat) end if class is(toml_table) if (has_key .and. policy%table == merge_policy%overwrite) then call lhs%delete(list(i)%key) has_key = .false. end if if (has_key .and. policy%table == merge_policy%append) then call lhs%get(list(i)%key, ptr2) select type(ptr2) class is(toml_table) call merge_table(ptr2, ptr1, policy) end select end if if (.not.has_key) then allocate(tmp, source=ptr1) tmp%origin = 0 call lhs%push_back(tmp, stat) end if end select end do end subroutine merge_table !> Append values from one TOML array to another recursive subroutine merge_array(lhs, rhs) !> Instance of array to merge into class(toml_array), intent(inout) :: lhs !> Instance of array to be merged class(toml_array), intent(inout) :: rhs class(toml_value), pointer :: ptr class(toml_value), allocatable :: tmp integer :: n, i, stat n = len(rhs) do i = 1, n call rhs%get(i, ptr) if (allocated(tmp)) deallocate(tmp) allocate(tmp, source=ptr) call lhs%push_back(tmp, stat) end do end subroutine merge_array end module tomlf_build_merge fortran-toml-0.5.0/src/tomlf/build/CMakeLists.txt0000664000175000017500000000135115201541453022073 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set(dir "${CMAKE_CURRENT_SOURCE_DIR}") list( APPEND srcs "${dir}/array.f90" "${dir}/keyval.f90" "${dir}/merge.f90" "${dir}/path.f90" "${dir}/table.f90" ) set(srcs "${srcs}" PARENT_SCOPE) fortran-toml-0.5.0/src/tomlf/build/array.f900000664000175000017500000010143515201541453020775 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Functions to build TOML arrays. !> !> This build module defines a high level interface to work with TOML arrays !> and construct them in a convenient way. !> !> The access to the array elements happens by position in the array, the indexing !> is one based, following the language convention of Fortran. All functions !> will only allow access of elements within the bounds of the array, specifying !> indices out-of-bounds should be save, as it only sets the status of operation. !> The getter functions allow access to other tables and arrays as well as !> convenient wrappers to retrieve value data !> !> The setter functions are somewhat weaker compared to the setter functions !> available for TOML tables. To limit the potential havoc this routines can !> cause they can only access the array within its bounds. Setting a value to !> another value will overwrite it, while setting a value to a table or an array !> will fail, for safety reasons. !> !> To (re)build an array appending to it is the best choice, tables and arrays !> should always be create by using the corresponding `add_table` and `add_array` !> function. While this can become cumbersome for values, the setter routines !> allow out-of-bound access to for the next element in an array and will indeed !> just append a new value to it. module tomlf_build_array use tomlf_build_keyval, only : get_value, set_value use tomlf_constants, only : tfc, tfi, tfr, tf_i1, tf_i2, tf_i4, tf_i8, & & tf_sp, tf_dp use tomlf_datetime, only : toml_datetime use tomlf_error, only : toml_stat use tomlf_type, only : toml_value, toml_table, toml_array, toml_keyval, & & new_table, new_array, new_keyval, add_table, add_array, add_keyval, & & cast_to_table, cast_to_array, cast_to_keyval, initialized, len implicit none private public :: get_value, set_value !> Setter functions to manipulate TOML arrays interface set_value module procedure :: set_elem_value_string module procedure :: set_elem_value_float_sp module procedure :: set_elem_value_float_dp module procedure :: set_elem_value_int_i1 module procedure :: set_elem_value_int_i2 module procedure :: set_elem_value_int_i4 module procedure :: set_elem_value_int_i8 module procedure :: set_elem_value_bool module procedure :: set_elem_value_datetime module procedure :: set_array_value_float_sp module procedure :: set_array_value_float_dp module procedure :: set_array_value_int_i1 module procedure :: set_array_value_int_i2 module procedure :: set_array_value_int_i4 module procedure :: set_array_value_int_i8 module procedure :: set_array_value_bool module procedure :: set_array_value_datetime end interface set_value !> Getter functions to manipulate TOML arrays interface get_value module procedure :: get_elem_table module procedure :: get_elem_array module procedure :: get_elem_keyval module procedure :: get_elem_value_string module procedure :: get_elem_value_float_sp module procedure :: get_elem_value_float_dp module procedure :: get_elem_value_int_i1 module procedure :: get_elem_value_int_i2 module procedure :: get_elem_value_int_i4 module procedure :: get_elem_value_int_i8 module procedure :: get_elem_value_bool module procedure :: get_elem_value_datetime module procedure :: get_array_value_float_sp module procedure :: get_array_value_float_dp module procedure :: get_array_value_int_i1 module procedure :: get_array_value_int_i2 module procedure :: get_array_value_int_i4 module procedure :: get_array_value_int_i8 module procedure :: get_array_value_bool module procedure :: get_array_value_datetime end interface get_value contains subroutine get_elem_table(array, pos, ptr, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Pointer to child table type(toml_table), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin class(toml_value), pointer :: tmp if (.not.initialized(array)) call new_array(array) nullify(ptr) call array%get(pos, tmp) if (associated(tmp)) then ptr => cast_to_table(tmp) if (present(stat)) then if (associated(ptr)) then stat = toml_stat%success else stat = toml_stat%type_mismatch end if end if if (present(origin)) origin = tmp%origin else if (present(stat)) stat = toml_stat%fatal if (present(origin)) origin = array%origin end if end subroutine get_elem_table subroutine get_elem_array(array, pos, ptr, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Pointer to child array type(toml_array), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin class(toml_value), pointer :: tmp if (.not.initialized(array)) call new_array(array) nullify(ptr) call array%get(pos, tmp) if (associated(tmp)) then ptr => cast_to_array(tmp) if (present(stat)) then if (associated(ptr)) then stat = toml_stat%success else stat = toml_stat%type_mismatch end if end if if (present(origin)) origin = tmp%origin else if (present(stat)) stat = toml_stat%fatal if (present(origin)) origin = array%origin end if end subroutine get_elem_array subroutine get_elem_keyval(array, pos, ptr, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Pointer to child value type(toml_keyval), pointer, intent(out) :: ptr !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin class(toml_value), pointer :: tmp if (.not.initialized(array)) call new_array(array) nullify(ptr) call array%get(pos, tmp) if (associated(tmp)) then ptr => cast_to_keyval(tmp) if (present(stat)) then if (associated(ptr)) then stat = toml_stat%success else stat = toml_stat%type_mismatch end if end if if (present(origin)) origin = tmp%origin else if (present(stat)) stat = toml_stat%fatal if (present(origin)) origin = array%origin end if end subroutine get_elem_keyval !> Retrieve TOML value as deferred-length character subroutine get_elem_value_string(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> String value character(kind=tfc, len=:), allocatable, intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_string !> Retrieve TOML value as single precision floating point number subroutine get_elem_value_float_sp(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Floating point value real(tf_sp), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_float_sp !> Retrieve TOML value as double precision floating point number subroutine get_elem_value_float_dp(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Floating point value real(tf_dp), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_float_dp !> Retrieve TOML value as integer value subroutine get_elem_value_int_i1(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value integer(tf_i1), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_int_i1 !> Retrieve TOML value as integer value subroutine get_elem_value_int_i2(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value integer(tf_i2), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_int_i2 !> Retrieve TOML value as integer value subroutine get_elem_value_int_i4(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value integer(tf_i4), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_int_i4 !> Retrieve TOML value as integer value subroutine get_elem_value_int_i8(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value integer(tf_i8), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_int_i8 !> Retrieve TOML value as boolean subroutine get_elem_value_bool(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value logical, intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_bool !> Retrieve TOML value as datetime subroutine get_elem_value_datetime(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value type(toml_datetime), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_datetime !> Retrieve TOML value as deferred-length character subroutine set_elem_value_string(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> String value character(kind=tfc, len=*), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_string !> Retrieve TOML value as single precision floating point number subroutine set_elem_value_float_sp(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Floating point value real(tf_sp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_float_sp !> Retrieve TOML value as double precision floating point number subroutine set_elem_value_float_dp(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Floating point value real(tf_dp), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_float_dp !> Retrieve TOML value as integer value subroutine set_elem_value_int_i1(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value integer(tf_i1), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_int_i1 !> Retrieve TOML value as integer value subroutine set_elem_value_int_i2(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value integer(tf_i2), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_int_i2 !> Retrieve TOML value as integer value subroutine set_elem_value_int_i4(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value integer(tf_i4), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_int_i4 !> Retrieve TOML value as integer value subroutine set_elem_value_int_i8(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value integer(tf_i8), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_int_i8 !> Retrieve TOML value as boolean value subroutine set_elem_value_bool(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Boolean value logical, intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_bool !> Retrieve TOML value as datetime value subroutine set_elem_value_datetime(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Datetime value type(toml_datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_datetime !> Retrieve TOML value as single precision floating point number subroutine get_array_value_float_sp(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Floating point value real(tf_sp), allocatable, intent(out) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it, info info = 0 allocate(val(len(array))) do it = 1, size(val) call get_value(array, it, val(it), info, origin) if (info /= 0) exit end do if (info /= 0) deallocate(val) if (present(stat)) stat = info if (present(origin) .and. info == 0) origin = array%origin end subroutine get_array_value_float_sp !> Retrieve TOML value as double precision floating point number subroutine get_array_value_float_dp(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Floating point value real(tf_dp), allocatable, intent(out) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it, info info = 0 allocate(val(len(array))) do it = 1, size(val) call get_value(array, it, val(it), info, origin) if (info /= 0) exit end do if (info /= 0) deallocate(val) if (present(stat)) stat = info if (present(origin) .and. info == 0) origin = array%origin end subroutine get_array_value_float_dp !> Retrieve TOML value as integer value subroutine get_array_value_int_i1(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value integer(tf_i1), allocatable, intent(out) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it, info info = 0 allocate(val(len(array))) do it = 1, size(val) call get_value(array, it, val(it), info, origin) if (info /= 0) exit end do if (info /= 0) deallocate(val) if (present(stat)) stat = info if (present(origin) .and. info == 0) origin = array%origin end subroutine get_array_value_int_i1 !> Retrieve TOML value as integer value subroutine get_array_value_int_i2(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value integer(tf_i2), allocatable, intent(out) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it, info info = 0 allocate(val(len(array))) do it = 1, size(val) call get_value(array, it, val(it), info, origin) if (info /= 0) exit end do if (info /= 0) deallocate(val) if (present(stat)) stat = info if (present(origin) .and. info == 0) origin = array%origin end subroutine get_array_value_int_i2 !> Retrieve TOML value as integer value subroutine get_array_value_int_i4(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value integer(tf_i4), allocatable, intent(out) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it, info info = 0 allocate(val(len(array))) do it = 1, size(val) call get_value(array, it, val(it), info, origin) if (info /= 0) exit end do if (info /= 0) deallocate(val) if (present(stat)) stat = info if (present(origin) .and. info == 0) origin = array%origin end subroutine get_array_value_int_i4 !> Retrieve TOML value as integer value subroutine get_array_value_int_i8(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value integer(tf_i8), allocatable, intent(out) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it, info info = 0 allocate(val(len(array))) do it = 1, size(val) call get_value(array, it, val(it), info, origin) if (info /= 0) exit end do if (info /= 0) deallocate(val) if (present(stat)) stat = info if (present(origin) .and. info == 0) origin = array%origin end subroutine get_array_value_int_i8 !> Retrieve TOML value as boolean subroutine get_array_value_bool(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value logical, allocatable, intent(out) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it, info info = 0 allocate(val(len(array))) do it = 1, size(val) call get_value(array, it, val(it), info, origin) if (info /= 0) exit end do if (info /= 0) deallocate(val) if (present(stat)) stat = info if (present(origin) .and. info == 0) origin = array%origin end subroutine get_array_value_bool !> Retrieve TOML value as datetime subroutine get_array_value_datetime(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value type(toml_datetime), allocatable, intent(out) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it, info info = 0 allocate(val(len(array))) do it = 1, size(val) call get_value(array, it, val(it), info, origin) if (info /= 0) exit end do if (info /= 0) deallocate(val) if (present(stat)) stat = info if (present(origin) .and. info == 0) origin = array%origin end subroutine get_array_value_datetime !> Retrieve TOML value as single precision floating point number subroutine set_array_value_float_sp(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Floating point value real(tf_sp), intent(in) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it class(toml_value), allocatable :: ptr do while(len(array) > size(val)) call array%pop(ptr) end do do it = 1, size(val) call set_value(array, it, val(it), stat, origin) end do if (present(origin)) origin = array%origin end subroutine set_array_value_float_sp !> Retrieve TOML value as double precision floating point number subroutine set_array_value_float_dp(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Floating point value real(tf_dp), intent(in) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it class(toml_value), allocatable :: ptr do while(len(array) > size(val)) call array%pop(ptr) end do do it = 1, size(val) call set_value(array, it, val(it), stat, origin) end do if (present(origin)) origin = array%origin end subroutine set_array_value_float_dp !> Retrieve TOML value as integer value subroutine set_array_value_int_i1(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value integer(tf_i1), intent(in) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it class(toml_value), allocatable :: ptr do while(len(array) > size(val)) call array%pop(ptr) end do do it = 1, size(val) call set_value(array, it, val(it), stat, origin) end do if (present(origin)) origin = array%origin end subroutine set_array_value_int_i1 !> Retrieve TOML value as integer value subroutine set_array_value_int_i2(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value integer(tf_i2), intent(in) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it class(toml_value), allocatable :: ptr do while(len(array) > size(val)) call array%pop(ptr) end do do it = 1, size(val) call set_value(array, it, val(it), stat, origin) end do if (present(origin)) origin = array%origin end subroutine set_array_value_int_i2 !> Retrieve TOML value as integer value subroutine set_array_value_int_i4(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value integer(tf_i4), intent(in) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it class(toml_value), allocatable :: ptr do while(len(array) > size(val)) call array%pop(ptr) end do do it = 1, size(val) call set_value(array, it, val(it), stat, origin) end do if (present(origin)) origin = array%origin end subroutine set_array_value_int_i4 !> Retrieve TOML value as integer value subroutine set_array_value_int_i8(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Integer value integer(tf_i8), intent(in) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it class(toml_value), allocatable :: ptr do while(len(array) > size(val)) call array%pop(ptr) end do do it = 1, size(val) call set_value(array, it, val(it), stat, origin) end do if (present(origin)) origin = array%origin end subroutine set_array_value_int_i8 !> Retrieve TOML value as boolean value subroutine set_array_value_bool(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Boolean value logical, intent(in) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it class(toml_value), allocatable :: ptr do while(len(array) > size(val)) call array%pop(ptr) end do do it = 1, size(val) call set_value(array, it, val(it), stat, origin) end do if (present(origin)) origin = array%origin end subroutine set_array_value_bool !> Retrieve TOML value as datetime value subroutine set_array_value_datetime(array, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Datetime value type(toml_datetime), intent(in) :: val(:) !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin integer :: it class(toml_value), allocatable :: ptr do while(len(array) > size(val)) call array%pop(ptr) end do do it = 1, size(val) call set_value(array, it, val(it), stat, origin) end do if (present(origin)) origin = array%origin end subroutine set_array_value_datetime end module tomlf_build_array fortran-toml-0.5.0/src/tomlf/CMakeLists.txt0000664000175000017500000000204115201541453020771 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. add_subdirectory("build") add_subdirectory("de") add_subdirectory("structure") add_subdirectory("type") add_subdirectory("utils") set(dir "${CMAKE_CURRENT_SOURCE_DIR}") list( APPEND srcs "${dir}/all.f90" "${dir}/build.f90" "${dir}/constants.f90" "${dir}/datetime.f90" "${dir}/de.f90" "${dir}/diagnostic.f90" "${dir}/error.f90" "${dir}/ser.f90" "${dir}/structure.f90" "${dir}/terminal.f90" "${dir}/type.f90" "${dir}/utils.f90" "${dir}/version.f90" ) set(srcs "${srcs}" PARENT_SCOPE) fortran-toml-0.5.0/src/tomlf/type/0000775000175000017500000000000015201541453017215 5ustar alastairalastairfortran-toml-0.5.0/src/tomlf/type/table.f900000664000175000017500000001431015201541453020623 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> TOML table data type !> !> A [[toml_table]] represents a TOML table (also known as a hash table or !> dictionary). Every TOML document contains at least one root table which !> holds key-value pairs, arrays, and nested tables. !> !> Tables are the primary way to access parsed TOML data. Use [[get_value]] !> from the build module to retrieve values by key, or the type-bound !> procedures for direct access. module tomlf_type_table use tomlf_constants, only : tfc use tomlf_error, only : toml_stat use tomlf_type_value, only : toml_value, toml_visitor, toml_key use tomlf_structure, only : toml_map_structure, new_map_structure implicit none private public :: toml_table, new_table, new, initialized !> TOML table type, extends(toml_value) :: toml_table !> Table was implictly created logical :: implicit = .false. !> Is an inline table and is therefore non-extendable logical :: inline = .false. !> Storage unit for TOML values of this table class(toml_map_structure), allocatable, private :: map contains !> Get the TOML value associated with the respective key procedure :: get !> Get list of all keys in this table procedure :: get_keys !> Check if key is already present in this table instance procedure :: has_key !> Append value to table (checks automatically for key) procedure :: push_back !> Remove TOML value at a given key and return it procedure :: pop !> Delete TOML value at a given key procedure :: delete !> Release allocation hold by TOML table procedure :: destroy end type toml_table !> Create standard constructor interface toml_table module procedure :: new_table_func end interface toml_table !> Overloaded constructor for TOML values interface new module procedure :: new_table end interface !> Check whether data structure is initialized properly interface initialized module procedure :: table_initialized end interface initialized contains !> Constructor to create a new TOML table and allocate the internal storage subroutine new_table(self) !> Instance of the TOML table type(toml_table), intent(out) :: self call new_map_structure(self%map) end subroutine new_table !> Default constructor for TOML table type function new_table_func() result(self) !> Instance of the TOML table type(toml_table) :: self call new_table(self) end function new_table_func !> Check whether data structure is initialized properly pure function table_initialized(self) result(okay) !> Instance of the TOML table type(toml_table), intent(in) :: self !> Data structure is initialized logical :: okay okay = allocated(self%map) end function table_initialized !> Get the TOML value associated with the respective key subroutine get(self, key, ptr) !> Instance of the TOML table class(toml_table), intent(inout) :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key !> Pointer to the TOML value class(toml_value), pointer, intent(out) :: ptr call self%map%get(key, ptr) end subroutine get !> Get list of all keys in this table subroutine get_keys(self, list) !> Instance of the TOML table class(toml_table), intent(inout) :: self !> List of all keys type(toml_key), allocatable, intent(out) :: list(:) call self%map%get_keys(list) end subroutine get_keys !> Check if a key is present in the table function has_key(self, key) result(found) !> Instance of the TOML table class(toml_table), intent(inout) :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key !> TOML value is present in table logical :: found class(toml_value), pointer :: ptr call self%map%get(key, ptr) found = associated(ptr) end function has_key !> Push back a TOML value to the table subroutine push_back(self, val, stat) !> Instance of the TOML table class(toml_table), intent(inout) :: self !> TOML value to append to table class(toml_value), allocatable, intent(inout) :: val !> Status of operation integer, intent(out) :: stat class(toml_value), pointer :: ptr if (.not.allocated(val)) then stat = merge(self%origin, toml_stat%fatal, self%origin > 0) return end if if (.not.allocated(val%key)) then stat = merge(val%origin, toml_stat%fatal, val%origin > 0) return end if call self%get(val%key, ptr) if (associated(ptr)) then stat = merge(ptr%origin, toml_stat%duplicate_key, ptr%origin > 0) return end if call self%map%push_back(val) stat = toml_stat%success end subroutine push_back !> Remove TOML value at a given key and return it subroutine pop(self, key, val) !> Instance of the TOML table class(toml_table), intent(inout) :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key !> Removed TOML value to return class(toml_value), allocatable, intent(out) :: val call self%map%pop(key, val) end subroutine pop !> Delete TOML value at a given key subroutine delete(self, key) !> Instance of the TOML table class(toml_table), intent(inout) :: self !> Key to the TOML value character(kind=tfc, len=*), intent(in) :: key call self%map%delete(key) end subroutine delete !> Deconstructor to cleanup allocations (optional) subroutine destroy(self) !> Instance of the TOML table class(toml_table), intent(inout) :: self if (allocated(self%key)) then deallocate(self%key) end if if (allocated(self%map)) then call self%map%destroy deallocate(self%map) end if end subroutine destroy end module tomlf_type_table fortran-toml-0.5.0/src/tomlf/type/meson.build0000664000175000017500000000115615201541453021362 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. srcs += files( 'array.f90', 'keyval.f90', 'table.f90', 'value.f90', ) fortran-toml-0.5.0/src/tomlf/type/keyval.f900000664000175000017500000002131515201541453021032 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> TOML key-value pair data type !> !> A [[toml_keyval]] represents a single key-value pair in a TOML document. !> It can hold any of the TOML value types: strings, integers, floats, !> booleans, and datetimes. !> !> Key-value pairs are typically accessed through their parent table using !> [[get_value]] rather than directly manipulating this type. module tomlf_type_keyval use tomlf_constants, only : tfc, tfr, tfi, toml_type use tomlf_datetime, only : toml_datetime use tomlf_type_value, only : toml_value, toml_visitor implicit none private public :: toml_keyval, new_keyval, new !> Generic TOML value type, abstract :: generic_value end type generic_value !> TOML real value type, extends(generic_value) :: float_value real(tfr) :: raw end type float_value !> TOML integer value type, extends(generic_value) :: integer_value integer(tfi) :: raw end type integer_value !> TOML boolean value type, extends(generic_value) :: boolean_value logical :: raw end type boolean_value !> TOML datetime value type, extends(generic_value) :: datetime_value type(toml_datetime) :: raw end type datetime_value !> TOML string value type, extends(generic_value) :: string_value character(:, tfc), allocatable :: raw end type string_value !> TOML key-value pair type, extends(toml_value) :: toml_keyval !> Actual TOML value class(generic_value), allocatable :: val !> Origin of value integer :: origin_value = 0 contains !> Get the value stored in the key-value pair generic :: get => get_float, get_integer, get_boolean, get_datetime, get_string procedure :: get_float procedure :: get_integer procedure :: get_boolean procedure :: get_datetime procedure :: get_string !> Set the value for the key-value pair generic :: set => set_float, set_integer, set_boolean, set_datetime, set_string procedure :: set_float procedure :: set_integer procedure :: set_boolean procedure :: set_datetime procedure :: set_string !> Get the type of the value stored in the key-value pair procedure :: get_type !> Release allocation hold by TOML key-value pair procedure :: destroy end type toml_keyval !> Overloaded constructor for TOML values interface new module procedure :: new_keyval end interface contains !> Constructor to create a new TOML key-value pair subroutine new_keyval(self) !> Instance of the TOML key-value pair type(toml_keyval), intent(out) :: self associate(self => self); end associate end subroutine new_keyval !> Deconstructor to cleanup allocations (optional) subroutine destroy(self) !> Instance of the TOML key-value pair class(toml_keyval), intent(inout) :: self if (allocated(self%key)) then deallocate(self%key) end if if (allocated(self%val)) then deallocate(self%val) end if end subroutine destroy !> Obtain real value from TOML key-value pair subroutine get_float(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(in) :: self !> Value to be assigned real(tfr), pointer, intent(out) :: val val => cast_float(self%val) end subroutine get_float !> Obtain integer value from TOML key-value pair subroutine get_integer(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(in) :: self !> Value to be assigned integer(tfi), pointer, intent(out) :: val val => cast_integer(self%val) end subroutine get_integer !> Obtain boolean value from TOML key-value pair subroutine get_boolean(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(in) :: self !> Value to be assigned logical, pointer, intent(out) :: val val => cast_boolean(self%val) end subroutine get_boolean !> Obtain datetime value from TOML key-value pair subroutine get_datetime(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(in) :: self !> Value to be assigned type(toml_datetime), pointer, intent(out) :: val val => cast_datetime(self%val) end subroutine get_datetime !> Obtain datetime value from TOML key-value pair subroutine get_string(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(in) :: self !> Value to be assigned character(:, tfc), pointer, intent(out) :: val val => cast_string(self%val) end subroutine get_string !> Obtain real value from TOML key-value pair subroutine set_float(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(inout) :: self !> Value to be assigned real(tfr), intent(in) :: val type(float_value), allocatable :: tmp allocate(tmp) tmp%raw = val call move_alloc(tmp, self%val) end subroutine set_float !> Obtain integer value from TOML key-value pair subroutine set_integer(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(inout) :: self !> Value to be assigned integer(tfi), intent(in) :: val type(integer_value), allocatable :: tmp allocate(tmp) tmp%raw = val call move_alloc(tmp, self%val) end subroutine set_integer !> Obtain boolean value from TOML key-value pair subroutine set_boolean(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(inout) :: self !> Value to be assigned logical, intent(in) :: val type(boolean_value), allocatable :: tmp allocate(tmp) tmp%raw = val call move_alloc(tmp, self%val) end subroutine set_boolean !> Obtain datetime value from TOML key-value pair subroutine set_datetime(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(inout) :: self !> Value to be assigned type(toml_datetime), intent(in) :: val type(datetime_value), allocatable :: tmp allocate(tmp) tmp%raw = val call move_alloc(tmp, self%val) end subroutine set_datetime !> Obtain datetime value from TOML key-value pair subroutine set_string(self, val) !> Instance of the TOML key-value pair class(toml_keyval), intent(inout) :: self !> Value to be assigned character(*, tfc), intent(in) :: val type(string_value), allocatable :: tmp allocate(tmp) tmp%raw = val call move_alloc(tmp, self%val) end subroutine set_string !> Get the type of the value stored in the key-value pair pure function get_type(self) result(value_type) !> Instance of the TOML key-value pair class(toml_keyval), intent(in) :: self !> Value type integer :: value_type select type(val => self%val) class default value_type = toml_type%invalid type is(float_value) value_type = toml_type%float type is(integer_value) value_type = toml_type%int type is(boolean_value) value_type = toml_type%boolean type is(datetime_value) value_type = toml_type%datetime type is(string_value) value_type = toml_type%string end select end function get_type function cast_float(val) result(ptr) class(generic_value), intent(in), target :: val real(tfr), pointer :: ptr nullify(ptr) select type(val) type is(float_value) ptr => val%raw end select end function cast_float function cast_integer(val) result(ptr) class(generic_value), intent(in), target :: val integer(tfi), pointer :: ptr nullify(ptr) select type(val) type is(integer_value) ptr => val%raw end select end function cast_integer function cast_boolean(val) result(ptr) class(generic_value), intent(in), target :: val logical, pointer :: ptr nullify(ptr) select type(val) type is(boolean_value) ptr => val%raw end select end function cast_boolean function cast_datetime(val) result(ptr) class(generic_value), intent(in), target :: val type(toml_datetime), pointer :: ptr nullify(ptr) select type(val) type is(datetime_value) ptr => val%raw end select end function cast_datetime function cast_string(val) result(ptr) class(generic_value), intent(in), target :: val character(:, tfc), pointer :: ptr nullify(ptr) select type(val) type is(string_value) ptr => val%raw end select end function cast_string end module tomlf_type_keyval fortran-toml-0.5.0/src/tomlf/type/value.f900000664000175000017500000001004015201541453020644 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Abstract base types for TOML values !> !> This module defines the abstract [[toml_value]] base type from which all !> concrete TOML types ([[toml_table]], [[toml_array]], [[toml_keyval]]) !> inherit. It also provides the [[toml_visitor]] abstract type for !> implementing the visitor pattern. !> !> Most users will not need to work with these types directly, but they !> are useful for implementing custom algorithms that traverse TOML !> data structures. module tomlf_type_value use tomlf_constants, only : tfc, TOML_BAREKEY use tomlf_utils, only : toml_escape_string implicit none private public :: toml_value, toml_visitor, toml_key !> Abstract base value for TOML data types type, abstract :: toml_value !> Raw representation of the key to the TOML value character(kind=tfc, len=:), allocatable :: key !> Original source of the value integer :: origin = 0 contains !> Accept a visitor to transverse the data structure procedure :: accept !> Get escaped key to TOML value procedure :: get_key !> Compare raw key of TOML value to input key procedure :: match_key !> Release allocation hold by TOML value procedure(destroy), deferred :: destroy end type toml_value !> Abstract visitor for TOML values type, abstract :: toml_visitor contains !> Visitor visiting a TOML value procedure(visit), deferred :: visit end type toml_visitor !> Thin wrapper around the deferred-size character intrinisc type :: toml_key !> Raw representation of the key to the TOML value character(kind=tfc, len=:), allocatable :: key !> Original source of the value integer :: origin = 0 end type toml_key abstract interface !> Accept a visitor to transverse the data structure recursive subroutine visit(self, val) import toml_value, toml_visitor !> Instance of the visitor class(toml_visitor), intent(inout) :: self !> Value to visit class(toml_value), intent(inout) :: val end subroutine visit !> Deconstructor to cleanup allocations (optional) subroutine destroy(self) import toml_value !> Instance of the TOML value class(toml_value), intent(inout) :: self end subroutine destroy end interface contains !> Accept a visitor to transverse the data structure recursive subroutine accept(self, visitor) !> Instance of the TOML value class(toml_value), intent(inout) :: self !> Visitor for this value class(toml_visitor), intent(inout) :: visitor call visitor%visit(self) end subroutine accept !> Get escaped key to TOML value subroutine get_key(self, key) !> TOML value instance. class(toml_value), intent(in) :: self !> Contains valid TOML key on exit character(kind=tfc, len=:), allocatable :: key if (allocated(self%key)) then if (verify(self%key, TOML_BAREKEY) == 0 .and. len(self%key) > 0) then key = self%key else call toml_escape_string(self%key, key) end if end if end subroutine get_key !> Compare raw key of TOML value to input key pure function match_key(self, key) result(match) !> TOML value instance. class(toml_value), intent(in) :: self !> TOML raw key to compare to character(kind=tfc, len=*), intent(in) :: key logical :: match if (allocated(self%key)) then match = key == self%key else match = .false. end if end function match_key end module tomlf_type_value fortran-toml-0.5.0/src/tomlf/type/CMakeLists.txt0000664000175000017500000000132515201541453021756 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set(dir "${CMAKE_CURRENT_SOURCE_DIR}") list( APPEND srcs "${dir}/array.f90" "${dir}/keyval.f90" "${dir}/table.f90" "${dir}/value.f90" ) set(srcs "${srcs}" PARENT_SCOPE) fortran-toml-0.5.0/src/tomlf/type/array.f900000664000175000017500000001221415201541453020653 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> TOML array data type !> !> A [[toml_array]] represents a TOML array, which is an ordered sequence !> of values. TOML arrays can contain values of any type, including nested !> arrays and tables (arrays of tables). !> !> Use [[get_value]] from the build module to retrieve array elements by !> index, or the type-bound procedures for direct access. The intrinsic !> [[len]] function is overloaded to return the number of elements. module tomlf_type_array use tomlf_error, only : toml_stat use tomlf_type_value, only : toml_value, toml_visitor use tomlf_structure, only : toml_list_structure, new_list_structure implicit none private public :: toml_array, new_array, new, initialized, len !> TOML array type, extends(toml_value) :: toml_array !> Is an inline array rather than an array of tables logical :: inline = .true. !> Storage unit for TOML values of this array class(toml_list_structure), allocatable, private :: list contains !> Get the TOML value at a given index procedure :: get !> Append value to array procedure :: push_back !> Remove the first element from the array procedure :: shift !> Remove the last element from the array procedure :: pop !> Release allocation hold by TOML array procedure :: destroy end type toml_array !> Create standard constructor interface toml_array module procedure :: new_array_func end interface toml_array !> Overloaded constructor for TOML values interface new module procedure :: new_array end interface !> Overload len function interface len module procedure :: get_len end interface !> Check whether data structure is initialized properly interface initialized module procedure :: array_initialized end interface initialized contains !> Constructor to create a new TOML array and allocate the internal storage subroutine new_array(self) !> Instance of the TOML array type(toml_array), intent(out) :: self call new_list_structure(self%list) end subroutine new_array !> Default constructor for TOML array type function new_array_func() result(self) !> Instance of the TOML array type(toml_array) :: self call new_array(self) end function new_array_func !> Check whether data structure is initialized properly pure function array_initialized(self) result(okay) !> Instance of the TOML array type(toml_array), intent(in) :: self !> Data structure is initialized logical :: okay okay = allocated(self%list) end function array_initialized !> Get number of TOML values in the array pure function get_len(self) result(length) !> Instance of the TOML array class(toml_array), intent(in) :: self !> Current length of the array integer :: length length = self%list%get_len() end function get_len !> Get the TOML value at the respective index subroutine get(self, idx, ptr) !> Instance of the TOML array class(toml_array), intent(inout) :: self !> Index to the TOML value integer, intent(in) :: idx !> Pointer to the TOML value class(toml_value), pointer, intent(out) :: ptr call self%list%get(idx, ptr) end subroutine get !> Push back a TOML value to the array subroutine push_back(self, val, stat) !> Instance of the TOML array class(toml_array), intent(inout) :: self !> TOML value to append to array class(toml_value), allocatable, intent(inout) :: val !> Status of operation integer, intent(out) :: stat if (allocated(val%key)) then stat = toml_stat%fatal return end if call self%list%push_back(val) stat = toml_stat%success end subroutine push_back !> Remove the first element from the data structure subroutine shift(self, val) !> Instance of the TOML array class(toml_array), intent(inout) :: self !> TOML value to be retrieved class(toml_value), allocatable, intent(out) :: val call self%list%shift(val) end subroutine shift !> Remove the last element from the data structure subroutine pop(self, val) !> Instance of the TOML array class(toml_array), intent(inout) :: self !> TOML value to be retrieved class(toml_value), allocatable, intent(out) :: val call self%list%pop(val) end subroutine pop !> Deconstructor to cleanup allocations (optional) subroutine destroy(self) !> Instance of the TOML array class(toml_array), intent(inout) :: self if (allocated(self%key)) then deallocate(self%key) end if if (allocated(self%list)) then call self%list%destroy deallocate(self%list) end if end subroutine destroy end module tomlf_type_array fortran-toml-0.5.0/src/tomlf/error.f900000664000175000017500000000636515201541453017717 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Error handling for TOML Fortran !> !> This module provides the [[toml_error]] type for error reporting and !> the [[toml_stat]] enumerator for status codes returned by various !> TOML Fortran procedures. !> !> ## Error Handling !> !> Most parsing and access functions accept an optional `error` argument !> of type [[toml_error]]. If an error occurs, this will be allocated !> and contain a descriptive message: !> !>```fortran !> type(toml_error), allocatable :: error !> call toml_load(table, "config.toml", error=error) !> if (allocated(error)) print '(a)', error%message !>``` !> !> ## Status Codes !> !> The [[toml_stat]] enumerator provides named constants for common !> error conditions like `toml_stat%duplicate_key` or `toml_stat%type_mismatch`. module tomlf_error use tomlf_constants, only : tfc, TOML_NEWLINE implicit none private public :: toml_stat, toml_error, make_error !> Possible TOML Fortran status codes type :: enum_stat !> Successful run integer :: success = 0 !> Internal error: !> !> General undefined error state, usually caused by algorithmic errors. integer :: fatal = -1 !> Duplicate key encountered integer :: duplicate_key = -2 !> Incorrect type when reading a value integer :: type_mismatch = -3 !> Conversion error when downcasting a value integer :: conversion_error = -4 !> Key not present in table integer :: missing_key = -5 end type enum_stat !> Actual enumerator for return states !> !> | Name | Description | !> |------|-------------| !> | `success` | Operation completed successfully | !> | `fatal` | Internal error or undefined error state | !> | `duplicate_key` | Duplicate key encountered in table | !> | `type_mismatch` | Incorrect type when reading a value | !> | `conversion_error` | Error when converting or downcasting a value | !> | `missing_key` | Requested key not present in table | type(enum_stat), parameter :: toml_stat = enum_stat() !> Error message produced by TOML-Fortran type :: toml_error !> Error code integer :: stat = toml_stat%fatal !> Payload of the error character(kind=tfc, len=:), allocatable :: message end type toml_error contains !> Create new error message subroutine make_error(error, message, stat) !> Error report type(toml_error), allocatable, intent(out) :: error !> Message for the error character(*, tfc), intent(in) :: message !> Status code integer, intent(in), optional :: stat allocate(error) error%message = message if (present(stat)) then error%stat = stat else error%stat = toml_stat%fatal end if end subroutine make_error end module tomlf_error fortran-toml-0.5.0/src/tomlf/datetime.f900000664000175000017500000002413515201541453020355 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> TOML datetime value representation !> !> This module provides the [[toml_datetime]] type for representing TOML !> datetime values. TOML supports four datetime formats: !> !> - Offset date-time: `1979-05-27T07:32:00Z` !> - Local date-time: `1979-05-27T07:32:00` !> - Local date: `1979-05-27` !> - Local time: `07:32:00` !> !> The [[toml_datetime]] type combines [[toml_date]] and [[toml_time]] !> components to represent any of these formats. module tomlf_datetime use tomlf_constants, only : tfc implicit none private public :: toml_datetime, toml_time, toml_date, to_string, has_date, has_time public :: operator(==) !> TOML time value (HH:MM:SS.sssssZ...) type :: toml_time integer :: hour = -1 integer :: minute = -1 integer :: second = -1 integer :: msec = -1 character(len=:), allocatable :: zone end type interface toml_time module procedure :: new_toml_time end interface toml_time !> TOML date value (YYYY-MM-DD) type :: toml_date integer :: year = -1 integer :: month = -1 integer :: day = -1 end type !> TOML datatime value type type :: toml_datetime type(toml_date) :: date type(toml_time) :: time end type !> Create a new TOML datetime value interface toml_datetime module procedure :: new_datetime module procedure :: new_datetime_from_string end interface toml_datetime interface operator(==) module procedure :: compare_datetime end interface operator(==) interface to_string module procedure :: to_string_datetime end interface to_string contains pure function new_datetime(year, month, day, hour, minute, second, msecond, zone) & & result(datetime) integer, intent(in), optional :: year integer, intent(in), optional :: month integer, intent(in), optional :: day integer, intent(in), optional :: hour integer, intent(in), optional :: minute integer, intent(in), optional :: second integer, intent(in), optional :: msecond character(len=*), intent(in), optional :: zone type(toml_datetime) :: datetime if (present(year) .and. present(month) .and. present(day)) then datetime%date%year = year datetime%date%month = month datetime%date%day = day end if if (present(hour) .and. present(minute) .and. present(second)) then datetime%time%hour = hour datetime%time%minute = minute datetime%time%second = second if (present(msecond)) then datetime%time%msec = msecond end if if (present(zone)) then datetime%time%zone = zone end if end if end function new_datetime pure function new_datetime_from_string(string) result(datetime) character(len=*), intent(in) :: string type(toml_datetime) :: datetime type(toml_date) :: date type(toml_time) :: time integer :: it, tmp, first character(*, tfc), parameter :: num = "0123456789" integer, allocatable :: msec(:) logical :: has_seconds first = 0 if (all([string(first+5:first+5), string(first+8:first+8)] == "-")) then date%year = 0 do it = first + 1, first + 4 tmp = scan(num, string(it:it)) - 1 if (tmp < 0) exit date%year = date%year * 10 + tmp end do date%month = 0 do it = first + 6, first + 7 tmp = scan(num, string(it:it)) - 1 if (tmp < 0) exit date%month = date%month * 10 + tmp end do date%day = 0 do it = first + 9, first + 10 tmp = scan(num, string(it:it)) - 1 if (tmp < 0) exit date%day = date%day * 10 + tmp end do first = first + 11 datetime%date = date end if if (first >= len(string)) return ! Check for time: HH:MM format (colon at position 3) if (string(first+3:first+3) == ":") then time%hour = 0 do it = first + 1, first + 2 tmp = scan(num, string(it:it)) - 1 if (tmp < 0) exit time%hour = time%hour * 10 + tmp end do time%minute = 0 do it = first + 4, first + 5 tmp = scan(num, string(it:it)) - 1 if (tmp < 0) exit time%minute = time%minute * 10 + tmp end do ! Check for optional seconds (TOML 1.1) has_seconds = first + 6 <= len(string) .and. string(first+6:first+6) == ":" if (has_seconds) then time%second = 0 do it = first + 7, first + 8 tmp = scan(num, string(it:it)) - 1 if (tmp < 0) exit time%second = time%second * 10 + tmp end do first = first + 8 else ! No seconds - keep time%second as default (-1) first = first + 5 end if if (first < len(string)) then if (string(first+1:first+1) == ".") then msec = [integer::] do it = first + 2, len(string) tmp = scan(num, string(it:it)) - 1 if (tmp < 0) exit msec = [msec, tmp] end do first = it - 1 msec = [msec, 0, 0, 0, 0, 0, 0] time%msec = sum(msec(1:6) * [100000, 10000, 1000, 100, 10, 1]) end if end if if (first < len(string)) then time%zone = "" do it = first + 1, len(string) time%zone = time%zone // string(it:it) end do if (time%zone == "z") time%zone = "Z" end if datetime%time = time end if end function new_datetime_from_string pure function to_string_datetime(datetime) result(str) type(toml_datetime), intent(in) :: datetime character(kind=tfc, len=:), allocatable :: str str = "" if (has_date(datetime)) then str = str // to_string_date(datetime%date) end if if (has_time(datetime)) then if (has_date(datetime)) then str = str // ' ' end if str = str // to_string_time(datetime%time) end if end function to_string_datetime pure function to_string_date(date) result(str) type(toml_date), intent(in) :: date character(:, tfc), allocatable :: str allocate(character(10, tfc) :: str) write(str, '(i4.4,"-",i2.2,"-",i2.2)') & & date%year, date%month, date%day end function to_string_date pure function to_string_time(time) result(str) type(toml_time), intent(in) :: time character(:, tfc), allocatable :: str integer :: msec, width character(1), parameter :: places(6) = ["1", "2", "3", "4", "5", "6"] ! Handle optional seconds (TOML 1.1) if (time%second < 0) then ! No seconds - output HH:MM format allocate(character(5, tfc) :: str) write(str, '(i2.2,":",i2.2)') & & time%hour, time%minute else if (time%msec < 0) then allocate(character(8, tfc) :: str) write(str, '(i2.2,":",i2.2,":",i2.2)') & & time%hour, time%minute, time%second else width = 6 msec = time%msec do while(mod(msec, 10) == 0 .and. width > 3) width = width - 1 msec = msec / 10 end do allocate(character(9 + width, tfc) :: str) write(str, '(i2.2,":",i2.2,":",i2.2,".",i'//places(width)//'.'//places(width)//')') & & time%hour, time%minute, time%second, msec end if if (allocated(time%zone)) str = str // trim(time%zone) end function to_string_time pure function has_date(datetime) class(toml_datetime), intent(in) :: datetime logical :: has_date has_date = (datetime%date%year >= 0) .and. & & (datetime%date%month >= 0) .and. & & (datetime%date%day >= 0) end function has_date pure function has_time(datetime) class(toml_datetime), intent(in) :: datetime logical :: has_time has_time = (datetime%time%hour >= 0) .and. & & (datetime%time%minute >= 0) end function has_time !> Constructor for toml_time type, necessary due to PGI bug in NVHPC 20.7 and 20.9 elemental function new_toml_time(hour, minute, second, msec, zone) & & result(self) integer, intent(in), optional :: hour integer, intent(in), optional :: minute integer, intent(in), optional :: second integer, intent(in), optional :: msec character(len=*), intent(in), optional :: zone type(toml_time) :: self if (present(hour)) self%hour = hour if (present(minute)) self%minute = minute if (present(second)) self%second = second if (present(msec)) self%msec = msec if (present(zone)) self%zone = zone end function new_toml_time pure function compare_datetime(lhs, rhs) result(match) type(toml_datetime), intent(in) :: lhs type(toml_datetime), intent(in) :: rhs logical :: match match = (has_date(lhs) .eqv. has_date(rhs)) & & .and. (has_time(lhs) .eqv. has_time(rhs)) if (has_date(lhs) .and. has_date(rhs)) then match = match .and. compare_date(lhs%date, rhs%date) end if if (has_time(lhs) .and. has_time(rhs)) then match = match .and. compare_time(lhs%time, rhs%time) end if end function compare_datetime pure function compare_date(lhs, rhs) result(match) type(toml_date), intent(in) :: lhs type(toml_date), intent(in) :: rhs logical :: match match = lhs%year == rhs%year .and. lhs%month == rhs%month .and. lhs%day == rhs%day end function compare_date pure function compare_time(lhs, rhs) result(match) type(toml_time), intent(in) :: lhs type(toml_time), intent(in) :: rhs logical :: match integer :: lms, rms lms = max(lhs%msec, 0) rms = max(rhs%msec, 0) match = lhs%hour == rhs%hour .and. lhs%minute == rhs%minute .and. lhs%second == rhs%second & & .and. lms == rms .and. allocated(lhs%zone) .eqv. allocated(rhs%zone) if (allocated(lhs%zone) .and. allocated(rhs%zone)) then match = match .and. lhs%zone == rhs%zone end if end function compare_time end module tomlf_datetime fortran-toml-0.5.0/src/CMakeLists.txt0000664000175000017500000000126015201541453017652 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. add_subdirectory("tomlf") set(dir "${CMAKE_CURRENT_SOURCE_DIR}") list( APPEND srcs "${dir}/tomlf.f90" ) set(srcs "${srcs}" PARENT_SCOPE) fortran-toml-0.5.0/meson.build0000664000175000017500000000377015201541453016475 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. project( 'toml-f', 'fortran', version: '0.5.0', license: 'Apache-2.0 OR MIT', meson_version: '>=0.55', default_options: [ 'buildtype=debugoptimized', 'default_library=both', ], ) install = not (meson.is_subproject() and get_option('default_library') == 'static') # General configuration information subdir('config') # Collect source of the project srcs = [] subdir('src') # TOML-Fortran library target tomlf_lib = library( meson.project_name(), sources: srcs, version: meson.project_version(), install: install, ) # Export dependency for other projects and test suite tomlf_inc = tomlf_lib.private_dir_include() tomlf_dep = declare_dependency( link_with: tomlf_lib, include_directories: tomlf_inc, ) # Package the license files tomlf_lic = files( 'LICENSE-MIT', 'LICENSE-Apache', ) if install # Distribute the license files in share/licenses/ install_data( tomlf_lic, install_dir: get_option('datadir')/'licenses'/meson.project_name() ) module_id = meson.project_name() / 'modules' meson.add_install_script( find_program(files('config'/'install-mod.py')), get_option('includedir') / module_id, ) pkg = import('pkgconfig') pkg.generate( tomlf_lib, description: 'A TOML parser implementation for data serialization and deserialization in Fortran', subdirs: ['', module_id], ) endif # add the testsuite if get_option('tests') fpm_toml = meson.current_source_dir()/'fpm.toml' subdir('test') endif fortran-toml-0.5.0/doc/0000775000175000017500000000000015201541453015071 5ustar alastairalastairfortran-toml-0.5.0/doc/reference/0000775000175000017500000000000015201541453017027 5ustar alastairalastairfortran-toml-0.5.0/doc/reference/index.rst0000664000175000017500000000042215201541453020666 0ustar alastairalastairReference documentation ======================= This section contains the detailed technical specification for TOML Fortran or the TOML language itself. .. toctree:: TOML specification API documentation fortran-toml-0.5.0/doc/requirements.txt0000664000175000017500000000007715201541453020361 0ustar alastairalastairablog sphinx-book-theme~=1.0.0 sphinx-copybutton sphinx-design fortran-toml-0.5.0/doc/_vendor/0000775000175000017500000000000015201541453016525 5ustar alastairalastairfortran-toml-0.5.0/doc/_vendor/sphinxcontrib_ansi.py0000664000175000017500000002042015201541453023001 0ustar alastairalastair# -*- coding: utf-8 -*- # Copyright (c) 2010, Sebastian Wiesner # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ sphinxcontrib.ansi ================== This extension parses ANSI color codes in literal blocks. .. moduleauthor:: Sebastian Wiesner """ from __future__ import absolute_import, print_function import logging import os from os import path import re from docutils import nodes from docutils.parsers import rst from docutils.parsers.rst.directives import flag from sphinx.util.osutil import copyfile from sphinx.util.logging import getLogger # DISABLED: from sphinx.util.console import bold __version__ = '0.7.0' # -- DIAGNOSTIC SUPPORT: LOGGER = getLogger(__name__) DIAG = os.environ.get("SPHINXCONTRIB_ANSI_DIAG", "no") == "yes" console = logging.getLogger("sphinxcontrib.ansi") console.setLevel(logging.ERROR) if DIAG: console.setLevel(logging.INFO) class ansi_literal_block(nodes.literal_block): """ Represent a literal block, that contains ANSI color codes. """ pass #: the pattern to find ANSI color codes COLOR_PATTERN = re.compile('\x1b\\[([^m]+)m') #: map ANSI color codes to class names CODE_CLASS_MAP = { 1: 'bold', 4: 'underscore', 30: 'black', 31: 'red', 32: 'green', 33: 'yellow', 34: 'blue', 35: 'magenta', 36: 'cyan', 37: 'white', 40: 'bg_black', 41: 'bg_red', 42: 'bg_green', 43: 'bg_yellow', 44: 'bg_blue', 45: 'bg_magenta', 46: 'bg_cyan', 47: 'bg_white', } class ANSIColorParser(object): """ Traverse a document, look for ansi_literal_block nodes, parse these nodes, and replace them with literal blocks, containing proper child nodes for ANSI color sequences. """ def _finalize_pending_nodes(self): """ Finalize all pending nodes. Pending nodes will be append to the new nodes. """ self.new_nodes.extend(self.pending_nodes) self.pending_nodes = [] def _add_text(self, text): """ If ``text`` is not empty, append a new Text node to the most recent pending node, if there is any, or to the new nodes, if there are no pending nodes. """ if text: if self.pending_nodes: self.pending_nodes[-1].append(nodes.Text(text)) else: self.new_nodes.append(nodes.Text(text)) def _colorize_block_contents(self, block): raw = block.rawsource # create the "super" node, which contains to while block and all it # sub nodes, and replace the old block with it literal_node = nodes.literal_block() literal_node['classes'].append('ansi-block') literal_node['classes'].append('highlight') block.replace_self(literal_node) # this contains "pending" nodes. A node representing an ANSI # color is "pending", if it has not yet seen a reset self.pending_nodes = [] # these are the nodes, that will finally be added to the # literal_node self.new_nodes = [] # this holds the end of the last regex match last_end = 0 # iterate over all color codes console.warn("\nANSI_COLORIZE.block:\n{}\nANSI_COLORIZE.block.end\n".format(raw)) for match in COLOR_PATTERN.finditer(raw): # add any text preceeding this match head = raw[last_end:match.start()] self._add_text(head) # update the match end last_end = match.end() # get the single format codes codes = [int(c) for c in match.group(1).split(';')] if codes[-1] == 0: # the last code is a reset, so finalize all pending # nodes. self._finalize_pending_nodes() else: # create a new color node code_node = nodes.inline() self.pending_nodes.append(code_node) # and set the classes for its colors for code in codes: if code in CODE_CLASS_MAP: code_node['classes'].append( f'ansi-{CODE_CLASS_MAP[code]}') # add any trailing text tail = raw[last_end:] self._add_text(tail) # move all pending nodes to new_nodes self._finalize_pending_nodes() # and add the new nodes to the block literal_node.extend(self.new_nodes) def _strip_color_from_block_content(self, block): content = COLOR_PATTERN.sub('', block.rawsource) literal_node = nodes.literal_block(content, content) block.replace_self(literal_node) def __call__(self, app, doctree, docname): """ Extract and parse all ansi escapes in ansi_literal_block nodes. """ handler = self._colorize_block_contents if app.builder.name not in ('html', 'dirhtml'): # strip all color codes in non-html output handler = self._strip_color_from_block_content for ansi_block in doctree.traverse(ansi_literal_block): handler(ansi_block) def add_stylesheet(app): if app.config.html_ansi_stylesheet: # -- RemovedInSphinx40Warning: # The app.add_stylesheet() is deprecated. # Please use app.add_css_file() instead. if hasattr(app, 'add_css_file'): app.add_css_file('ansi.css') else: # -- SUPPORTS: sphinx < 2.0 ? app.add_stylesheet('ansi.css') def copy_stylesheet(app, exception): if app.builder.name not in ('html', 'dirhtml') or exception: return verbose = hasattr(app, 'info') stylesheet = app.config.html_ansi_stylesheet if stylesheet: if verbose: # DISABLED; LOGGER.info(bold('Copying ansi stylesheet... '), nonl=True) LOGGER.info('Copying ansi stylesheet... ', nonl=True) dest = path.join(app.builder.outdir, '_static', 'ansi.css') source = path.abspath(path.dirname(__file__)) copyfile(path.join(source, stylesheet), dest) if verbose: LOGGER.info('done') class ANSIBlockDirective(rst.Directive): """ This directive interprets its content as literal block with ANSI color codes. The content is decoded using ``string-escape`` to allow symbolic names as \x1b being used instead of the real escape character. """ has_content = True option_spec = dict(string_escape=flag) def run(self): text = '\n'.join(self.content) if 'string_escape' in self.options: text = text.decode('string-escape') return [ansi_literal_block(text, text)] def setup(app): app.require_sphinx('1.0') app.add_config_value('html_ansi_stylesheet', None, 'env') app.add_directive('ansi-block', ANSIBlockDirective) app.connect('builder-inited', add_stylesheet) app.connect('build-finished', copy_stylesheet) app.connect('doctree-resolved', ANSIColorParser()) return { 'version': __version__, 'parallel_read_safe': True, 'parallel_write_safe': True, } fortran-toml-0.5.0/doc/news/0000775000175000017500000000000015201541453016045 5ustar alastairalastairfortran-toml-0.5.0/doc/news/2022-08-jonquil.rst0000664000175000017500000000324215201541453021071 0ustar alastairalastair:author: Sebastian Ehlert :date: 2022-08-03 :category: release :excerpt: 1 Jonquil: Bringing TOML blooms to JSON land ========================================== .. figure:: ../_static/img/jonquil.svg :alt: Jonquil Blossom :align: center :width: 50% Jonquil started out of the idea to make a TOML parser speak JSON. First explored as a way to connect to the toml-test validation suite, the implementation was brief and expressive enough to be explored as a tutorial. Since the implementation was taken serious enough to enter into JSON Fortran's benchmarks and turned out to actually be competitive with JSON Fortran, revisiting the idea of a JSON parser in TOML Fortran was warranted. Jonquil aims to provide a compatibility layer to allow seamless usage of both TOML and JSON in the same project. Based on the TOML Fortran API a JSON lexer is implemented to connect the JSON grammar with the existing TOML parser. Just a parser however does not make a library, Jonquil provides a flavor of the public TOML Fortran API for consistency, retaining full compatibility with TOML Fortran even on the ABI level. Everything you can do with TOML Fortran is also possible with Jonquil, and they can be mixed and matched. References ---------- - `Jonquil repository `__ - `JSON Fortran repository `__ - `rojff repository `__ - `toml-test JSON encoding `__ - `JSON Fortran's parser benchmark `__ - Tutorial: :ref:`json-lexer` - Recipe: :ref:`jonquil` fortran-toml-0.5.0/doc/news/2026-03-release-0.5.0.rst0000664000175000017500000000422415201541453021466 0ustar alastairalastair:author: Sebastian Ehlert :date: 2026-03-05 :category: release :excerpt: 1 TOML Fortran 0.5.0 released =========================== Support for TOML 1.1.0 is here! With this release TOML Fortran introduces full support for the new TOML release, which adds support for newlines and trailing commas to inline tables, new escape sequences, and extends the datetime format with making seconds optional. The documentation of TOML Fortran was improved with adding installation instructions for Debian and AUR, which started to package TOML Fortran. Furthermore, the examples now include cases for handling defaults and absence of tables and arrays. Full changelog -------------- Full commit history available at `v0.4.3...v0.5.0 `__. For release artifacts, like source distributions, checkout `v0.5.0 `_. New features ~~~~~~~~~~~~ * Add \e escape sequence (`toml-f#171 `__) * Add \x escape sequence (`toml-f#170 `__) * Support newlines and trailing comma in inline tables (`toml-f#99 `__) * Support optional seconds in datetime and add edge case tests (`toml-f#175 `__) Documentation ~~~~~~~~~~~~~ * Add handling of default arguments to table documentation (`toml-f#173 `__) * Update documentation (`toml-f#168 `__, `toml-f#167 `__, `toml-f#174 `__, `toml-f#178 `__) * Add Debian installation documentation (`toml-f#176 `__) * Document TOML Fortran availability in Arch Linux AUR (`toml-f#180 `__) Bugfixes ~~~~~~~~ * Fix datetime out of bounds access in (`toml-f#172 `__, `toml-f#182 `__) fortran-toml-0.5.0/doc/news/2023-04-release-0.4.0.rst0000664000175000017500000000340515201541453021463 0ustar alastairalastair:author: Sebastian Ehlert :date: 2023-04-01 :category: release :excerpt: 1 TOML Fortran 0.4.0 released =========================== This release refactors the internal access of the storage structures to allow more efficient operations like deletion or renaming. Additionally, the serialization functionality has been reworked to allow writing to files, units or strings using similar interfaces. .. admonition:: TOML Fortran is looking for support! :class: attention If you are interested in contributing to the project, please checkout the `repository `__, or reach out in `toml-f#62 `__ to the current maintainer. Full changelog -------------- Full commit history available at `v0.3.0...v0.4.0 `__. For release artifacts, like source distributions, checkout `v0.4.0 `_. Library changes ~~~~~~~~~~~~~~~ * Refactor storage structure for tables and arrays (`toml-f# `__) * Refactor serialization of TOML data (`toml-f# `__) * Support nil tokens in parser (`toml-f# `__) Bugfixes ~~~~~~~~ * Fix missing recursive attribute required (`toml-f# `__) * Return a missing key error if value is requested without default (`toml-f# `__) * Use -Werror for in CI testing (`toml-f# `__) Documentation updates ~~~~~~~~~~~~~~~~~~~~~ * Add recipe on Jonquil for JSON support (`toml-f#109 `__) fortran-toml-0.5.0/doc/news/2023-12-release-0.4.2.rst0000664000175000017500000000303015201541453021456 0ustar alastairalastair:author: Sebastian Ehlert :date: 2023-12-03 :category: release :excerpt: 1 TOML Fortran 0.4.2 released =========================== Maintenance and bugfix release to address issues found for GFortran on MacOS/ppc32 and Cray compilers. Furthermore, parsing the header of an array of tables with whitespace and the tokenization of multiline strings with escape characters was fixed. .. admonition:: TOML Fortran is looking for support! :class: attention If you are interested in contributing to the project, please checkout the `repository `__, or reach out in `toml-f#62 `__ to the current maintainer. Full changelog -------------- Full commit history available at `v0.4.0...v0.4.2 `__. For release artifacts, like source distributions, checkout `v0.4.2 `_. Bugfixes ~~~~~~~~ * Don't use ``ieee_arithmetic`` in library to support GFortran on MacOS/ppc32 (`toml-f#133 `__) * Remove associate construct which breaks for Cray (`toml-f#137 `__) * Fix for change in cmake behaviour (`toml-f#139 `__) * Fix flagging of whitespace in array of table header (`toml-f#142 `__) * Add test for multiline string with escaped quote (`toml-f#146 `__) fortran-toml-0.5.0/doc/news/index.rst0000664000175000017500000000006315201541453017705 0ustar alastairalastair.. _news: News ==== .. will be replaced by ablog fortran-toml-0.5.0/doc/news/2022-08-release-0.3.0.rst0000664000175000017500000001063615201541453021471 0ustar alastairalastair:author: Sebastian Ehlert :date: 2022-08-01 :category: release :excerpt: 1 :image: 1 TOML Fortran 0.3.0 released =========================== .. figure:: ../_static/img/release-0.3.0.png :alt: Features spotlight :align: center :width: 75% This release comes with a new backend for the deserialization of TOML documents, making the parser more robust against unclosed inline tables and arrays. Furthermore, a context can now be captured which allows reporting messages like errors as annotations in TOML document, making both the default errors produced by the parsers more useful and also enabling users of TOML Fortran to create consistently formatted reports. Together with the refactoring of the internal representation for storing TOML values, the low-level interface to TOML values becomes more consistent and easier to use. A long-standing issue with supporting Unicode escape sequences in strings has been resolved and TOML Fortran now fully supports all TOML 1.0.0 escape sequences. The high-level interface to the TOML data structures has been extended to return origin information for each access, which can be used together with the context captured while parsing for better error reporting. An additional set of interfaces was added allowing easier access to deeply nested values by internally transversing the data structure, rather than requiring the user to do it manually. Similarly, whole arrays can now be retrieved directly into a one-dimensional allocatable array. Many thanks to Kjell Jorner (`@kjelljorner `__), Emily Kahl (`@emilyviolet `__), Asdrubal Lozada-Blanco (`@aslozada `__) and Daniel Mejia-Rodriguez (`@dmejiar `__) for contributing to this release. .. admonition:: TOML Fortran is looking for support! :class: attention If you are interested in contributing to the project, please checkout the `repository `__, or reach out in `toml-f#62 `__ to the current maintainer. Full changelog -------------- Full commit history available at `v0.2.3...v0.3.0 `__. For release artifacts, like source distributions, checkout `v0.3.0 `_. Library changes ~~~~~~~~~~~~~~~ * Improve lexing and parsing of TOML documents (`toml-f#88 `__) * Refactoring of internal storage and encoding structure (`toml-f#93 `__) * Allow choice of merge policy and remove context from merged data (`toml-f#94 `__) * Support for Unicode escape sequences (`toml-f#100 `__) * Allow fetching of nested values using a key path (`toml-f#104 `__) * Add whole array setters and getters to build interface (`toml-f#79 `__) Repository Maintenance ~~~~~~~~~~~~~~~~~~~~~~ * Use proper upstream for test-drive (`toml-f#58 `__) * Update README, documentation workflow and validation suite (`toml-f#73 `__) * Update install-mod.py to use python3 (`toml-f#60 `__) * Update CI workflow (`toml-f#87 `__) Documentation Updates ~~~~~~~~~~~~~~~~~~~~~ * Add tutorial on creating a linter (`toml-f#88 `__) * Add tutorial on writing a custom JSON lexer (`toml-f#93 `__) * Update documentation (`toml-f#78 `__, `toml-f#80 `__) * Add recipe for handling an array of tables (`toml-f#82 `__) * Added recipe for reading elements of an array, with error checking (`toml-f#81 `__) * Update German translation (`toml-f#95 `__) * Spanish translation/Traducción al español (`toml-f#86 `__) * Add recipe on date time compatibility with other libraries (`toml-f#103 `__) fortran-toml-0.5.0/doc/conf.py0000664000175000017500000000400215201541453016364 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys, os.path as op sys.path.insert(0, op.abspath(op.join(op.dirname(__file__), "_vendor"))) project = "toml-f" author = "Sebastian Ehlert" copyright = f"2019-2026, {author}" version = "0.5.0" release = version extensions = [ "ablog", "sphinx_design", "sphinx_copybutton", "sphinx.ext.intersphinx", "sphinxcontrib_ansi", ] html_theme = "sphinx_book_theme" html_title = "TOML Fortran" html_logo = "../assets/toml-f.svg" html_favicon = "../assets/toml-f.svg" _extra_navbar = """ """ html_theme_options = { "repository_url": "https://github.com/toml-f/toml-f", "repository_branch": "main", "use_repository_button": True, "use_edit_page_button": True, "use_download_button": False, "path_to_docs": "doc", "extra_navbar": _extra_navbar, } html_css_files = ["css/custom.css"] html_static_path = ["_static"] templates_path = ["_templates"] locale_dirs = ["locales"] copybutton_selector = "div:not(.no-copybutton) > div.highlight > pre" blog_path = "news/index" blog_post_pattern = "news/**" master_doc = "index" fortran-toml-0.5.0/doc/_static/0000775000175000017500000000000015201541453016517 5ustar alastairalastairfortran-toml-0.5.0/doc/_static/img/0000775000175000017500000000000015201541453017273 5ustar alastairalastairfortran-toml-0.5.0/doc/_static/img/release-0.3.0.png0000664000175000017500000057166115201541453022075 0ustar alastairalastairPNG  IHDR1DSz pHYsnu>tEXtSoftwarewww.inkscape.org< IDATx]w\G~8:( [5jkc#*cƚ|j411jTT챡{ *J~w۽+=anggygޙ!H 6` 6` 6` 6GP&%%PTP@yNJYT3l,DK>>>RD@HLb> L&AIN2IHllll$IH $C,$P0 P6` 6$A;PAF)))@{F lllP $Imi ""¾tC,P` 6$\ l0l}$lllG֥mZjCbb$IP ( ( d2d2 $16qnےi&)?,Umoʒq$I$A$T*7 AķBMb6G&A.( 0Z߇`8.AɋVJ:UVgpjaC69xaITy& `z5eJ6${)J(Jىʟ?~=fӒ,ih [,LR,TUT?o qV,$wJP输dP*P( Li URi$Bj‚E%Ѭj[2{H`ydBM j/t!H{΀%/ Ă9AaZʏijk{$ ^^^̭d}P_ԣ ҏ\K5!i!i Ij-:X3Ҙ5%0-q ??𽧧ߜDb&&&'Ir?z P*x^VB$ϓ#.@A\LtHTG .XLh:Ǭ[M -jY!nGG'a[ k$G.X骮uhMY`>[c(fIG-RbHƆX#,Bj.0Li$*j,# !78 ,H.Y|☄a.= opJaggg&OXUX0d0/Y2L,[20l A$S^^^}:npL& rrH+5L#7}Tq)PI5ߊ-4gn-N3CrlRl}<==!E9"2 ή+ Pی081$ G a\oi22 e, ,d @ %gflƍB ]m5ixZ\d!dcLoLLBPz40xaeaE1+"K)#L%b$&d*%e۸ NX+!1ͥ4,2UA|qZČ.cggw@@ Ax>xu5(a6IeRayg ̠-V |Jt M3qF|ҪahIm?|ґ*l T%$$`Er$E%9)1gd*CS7{V6HLN\IQ#U 1$K °sD/0+0SNp faXH]bYI Inc\bA ,b/`,xB3S|:֕T/HD^^$4 @3OOP㊋P&#|m#jh}$*uk'Q5$ HՍ }MwJG%d9g7OOϙ 8L)ciLP1p%ZOAZ* "ޙM vpGL@YAY\ =IV;.ǗLMJ OVzæ! i0,yxx4VnZZgnn[J@mPq5֙7@fd؀- I0-t5A6KZDI bCQ`ϣu~i*852i+񥇇j$Ƴb!uFIY0`:K0+8[c֎φDjgA+2G"vPBl +[h qM}K1PL*k*e sef ZTL%_ O ɴ֘$I0Dd2 1]418d+Ejtt'E&ʬ`BкP215X V#DH舙|8+Ar.  r5&I_PAxT)AdFj[&,-\Wa8`\3'dȰΝqIdz"!HY{tޥ6A-yɗ"+I+z˜~AeB%Š |tX'CVb$D֓r\wKyׄ Q$淚4@l$iu5tOmn}V4 ޢi46$b!M"c0j[Mv;L.+&CJpzb6-63GTVyhDDg1iVY2B_Ů.X׾5ChdI- 2${`H4r?u;\C%jNgKD:jc j5jt@W!/K`qjtD?RA14F7J#.I&NsPXj47V0zIeAR4uCm,9DrV i`gjFw_Aj9wshtX>oe2 4`_ Oͳ\\MKU]` $#+A_ϟ:iPͳ$S>96sGT<9mY tj) ,Z*d2vK&W5u At"WwAW,F*VņX7Y!L 66a[Yk2R, -VF{ЗJ:# Kԇf}~,cυ +@,kKbHq5?(k$>mspCZHL',>$5d%b*5xI>-'Vt%P 놁05ιʵ2FCPc'&L!f*Eg%,婷C,bӭ%KC+.afy!rz֓~Mi"Z.jqCu~ɃL3˯1U/ `:dޞPIT ԀӟЁ}_~~~m*%` H}/>tqQ6]j!c)DMb19O )}h ZGIQ Z;PN(b TH{ REµb)ȕzTG$)1`%,$ PGC $.ֱe81xS.-G8XxTԩS.,Uyb9~m>AD[ 4* /?G|Bի WW73,d KGX^&4+EW&cKH1=P mLl݌22l=.H>ZEP㡏3x.R$_Ltfb$LNk9C'`(䫒$Iz)p00Ӂ^UYcHk_]\", G3.HÍUp(儏O#}QuUcxbs ڈvg&%ERA2"yP|PJ*5k5b-މ30;^xÇ޽W77J1B ţ,$OSDEEaqM3?~i#V> Dɱʔw%Ze$I3A8h&$I9ygEg^VH+.C*6K3!!DCQO4CƤd%3zUR /$q즁^"Mt'mP[#IHzY Ervr&~ŻHGNzp(G'x*FQ5*3`~ 0Dexh)R0A2h LHNN hժ*T/JǑdsC|ngC&޽8|ܸqq(PWG5G]PvmZ G1 ɓ&j LHKM)h٢%jԬ m}+L3k LWD-ШQ#0{+UVhӶ-T$mېDq2e EU… s^-Z4ϲUk3C%AU+`a$ՅӣH,L9%^i .; ^wIOa#eQ2szǩn$ͭd(5NT$''ӂ ?޲by8 Oe {JreN&| ?ĈTsfaHMiidv:Wx<q]6 ёﹳ9} mي1%m=h&V }!9zoE:-@[Oeɔ dgg3|xzz}Gnn.!hڴqbӿ~ V,GNM1ݺ}bLR W$''1ޱgl $@233=< GR6HQtYXbXQħc"(H?TLg=$_.*>;AB Am\HN=0HJOIk,)@tk$0N=km;}'Ŗd[_EdJb;$ѫ(E*PC D=lpTJ45 5#eKq)Ƌ/Q)n iiUC*K93—Zg <Kq|a,dggSb寿P|4om?@M~EÓ?!:RG6|6fMSNΝ - A`̙Xd1Ž~֭m3cL|l5jo~aa$2[!4o]%.Z+zX{,zCP4.l|CKx5l#IRIz Z3Amd j%2u!ªCIóbETj|^pܡ hGod#3>q§i%q}bAC?0rh~ZC d\pU[j)T*y$.[F#0823Ѣe+L;;;"D=uۼ4شq=#\O{aeA ׸ZkLֈ #K34P/EH_Eup0죏?y2"߆c*.$Ojի㟃&Mb)pv>joȐ!hԨ1-8k9a[^z7c0NEbb"ʔ)uiӦTT ÇUK.prrBҥ9BGdd$_ؘr-[۴AٲeEJMM-)) NNAÆ A ſAlum'gmu $c'*rNbTf@&$vP ӧ~RR"ƌӾiҤ)vUt@ƦA(6_T*$ \n^1Z 0iUA3?E'k,Eǟhd6bPE5I˃RP+FGc5jNNN<,mMLyő(!:zJt\f@i$TRRXZs%8TWPPCA$x 'i$ICiggp 2עpTRBKWh抏^ (L (>i`(jNI/ϸPO jdZWāO?G2 >˗/G )۷oc9Q1|}sua[bӆ x2e`I:u*\܊^lwOKM_{͛((`?m۶X|9Zjd<~ZԺ![:u -BXh(EB.['ggZLjÇ]`hܸmepYQ 4V:ǣ AW;Y5"vy&N"++ ;odž i8kQ:XupȊv{~$"GڃI‹3OKdĤ (׼2jl{7mmQ!/&Ed?q)*LVV9xy ~I(·\)K9w4jù(9 =x׼EK;YJ7K'[,J%Ely Juyݟ0rNHĉc8o\$ OO/4o>I9Oñ|bDP3Q/ =ft=ۍ1`D>?} >URغenߺhۣJ5f,OS.I8}߇+AJ/2hQs  ;;fe@Ǻ puuCcضm ^ BzzKv0 :v,(ݩ)ضu3NĽ{wT}~~>A9rHڻO? z??m?Ir{Hq?bŊ7I}^7xyy!== [n? I232PL|Iw_PЂ,<~ Ǐŵk~999prrBUЩsg|1b$7<|YYHNNWpYMq;O}֬Ynq.G_232s?v wAjjڭv:iS) /(W232m6ؿaaOOOOt /?[$>}={vK CZZ*$Zܹ 58API ӧHLLIpuuCÆ 駽0fX'Y_ΤV:.]uRRfLFqڵ+FzHO"5~..hذ!z쉱|!6lܸW8½;֮C_ vlߎS'SΛ  K.o\^`?I 4h=zK,M>NnooDŋ gglǣo~@سg&M@q={6,YʛV1eHHvޅ/"** yyypwwGFЫW/;N f&ص]SEbIBR!rד$9vv,ߺvdgIƊ]DHOK??-۶~lތ/g$pvv YZ76Bw?TZGYk~[~II̋٠P(~ 6L%<@Ա`~cqN,##S&Ogoo6``wڅiSrt899ٳ1w<~y+=޽{7v,95k`s B}_BBwp% Ӧa^01bN?ĉ9K:0c ,Z- cqddP*N+ I99E`#vIN^vjWVwH$=O-!ȡJӡxw5-YD|.F ^ߥ sJp 1#χᮛϢ6RHLDJd"րK8Dpu\x:waWo|~TPII@nn.nٌ &uti8|ǎ]K(4qqW eÇR¸RHL~9#숑!-ģq?ҥ=0`HJJX9}?;;O8{|Ǎ­n28}V{@͚u}G~ګv܁K.Pd$#1?3fa?}5s:RRmCvv6?~Ǐak0mX(ryhcJ9;`HMI8y"Gxܼy;oŅ yq=]Υ]啩Ec! -=S' ::766vnlj8un#55ƌbwt"W^^eLhl߶ϛ7o.z~ muccc)9W\aۼ PIǑTE{8_D?DEEQaϞ gРACt;w3OūHƻL",,\~`Q_ű۷o[vü25^3 !:y'!..➓'~=S]G SLǰDMHH}!00'NI&(((uZ!..7_ڵǷ0k,r{Nj<{>LKKŵkWqUY۶D(:xyyٳ\.ԩ ,2qQ>|6z~yyػgV^Ykk`ؑ}0~-F\ * 4݃UVqZ#$$!!Xv 6oي] sNqHqy 27l4B|a? 'N`KIIAPPzjl߱~1c`>Lc(4 y,aذa8qcvv6ƍ/qc3~ Ɏ۷"05iT* I"p!ׯ2҅_rk5EL`6vhҤ jשC!DpxFSc6`V͛3?|P {JIR`p0%֭[ӻ7RSS舊,ӻ7bbbXB1q]/\@i-N%05E'0R닀#GÔɓQjU֋X/߰\.4sV9ˊKǃmbBLC w ޤࣩWDEDS'~R󹹹ϱ{-0yu7ooHOOѣ֥=._ 7<<:b z[H`-1c޴T*lܸ?‘cg!2iXznܸ;LKKŤ~a֬غe"(2}Txޖi̝`4h D1r]PP?UL,W|ܺu f5 B!hԈ{Wb1cCBBgrHLLdt*~Qfa۷oѭ[WD,~}"00P?ɯ'^
|ϝٳg}dB$''cСfڵѩS'xxx &:OFTT]\kn֋3;on1nРwJ*!==É'(i61qYfhݦ paƖ׮Ō3;ttthZ݆]x۶n.raٸp6&Va^0eTxzz!5%=ӧݻcL]wcܾ46m0Q6t(+Aҥ <=<3gիWpttĮݻ¼Ho?tttĀPR%<рӧO1<߰.//_Μ+* t?OqE#زy3tppqжMraMr+((ٳq-8ml۶qBn0cL)Siix.\ӧN!;;:tܹskTKL3iuQ>dƆ^#31NH:{_ԵyYD]{ʥPeN [B}||j0t-GACgqgߠ!\]Cjb8p\VN_}~;xs~Ѯk@AK o^I~zI3I8 ؟A`:88AFpuqţG[ԩ$$x?yF fK.-[CӓzΩXBVynnHOOc;y"˖|˿71u$^*$EOCu2dÍ!w.s] Fj}{q(wqqZ8::r5 ji p㐡˂Ts6n޸-Z0mݲ˖- , 1^XPh :~0ksP YVV&cvJfϞ%h+¶?~׮]Ǵ65,,A`6m5/{ m09 cƌU+)֧2 eʔ\.GLL #2320\;NJb&&&ŋC2FU+X%/3,_ __b/YYY8{]^ٱ._'FOD!|yyy S.AӧO)___ܹse)HWz6GLÊENIָ\Iݽ3gR,zcW_iݮ!,4uRw܄3-—.]5=i&NDtt4\dP*m9.''7mBrPV-(@ҥ1ܽ{G1\8'OJ{iRĚk1bH ̞5ڶiM!겲p3F[Ig1Po>qT$~aF>m͛7qr+ʓ,hР4uCll,M^xQ„>~>{Ou,Zo߾) r%ƎK\ ɟy е[7;w֨G#66VT)1 N5;pqKLLq92BCC$,lÄxTZ۵پ}E:o 1u'$+Wnݺ#G25q^$ c*D\r֭z]lD<`X::ߟ}P]p֭Ou?KbuI[FbX0fܿY`뇿]Mv튞={K.purPzadyO5<`sg/_KGgiTT /K hHHr0/ܱ 1W^Bb@7Z+ZqDٲлO_xxPIVbo;x&'%aXdl֍Xm5Ԗ)ggoOXUk@!1\PvQf-ܿw$EjT _]ydHu`5oPH; >k`a ;9O1>|$덀~)$&>} sߖK/}!{0;#^svv烇2Ǝlui[R v? hwbB"| mK%u, RI!05hޜm<-y -=҂uPlI |0ܘ &Lp%<4-i@ҘCe[gΜիQС#:um?R\.\.7Kkr|gLL >|Ǐ׈#nX.',з'&&2 c@.SX }WG1EbACլYK/#--M$I >}ʸLIHLJB"Eu֣ܤje,?3:JDo7H+~;yX7ŲaÆQ?BtKëLJuy˒FQh|ς~(TXFBmD=o,41^w.ݳKqZjf/^ؿOV3l\*:&!!rf sI5:'>ϟ1-׸PFM,XXk!߬-/R5lԘݻw/^\P`aX[+Awe1VrB 0aM8G Mx8!!!7mD@<|PPzl]6pwwl#IqE0۪CоM6n4D!am\^:+F] wCL ~OR&D.c]ؼi#~'Vb(l>;v'|BIDvV&#=ɠW0d`+T@BB+e({;;~57o݊q$%s NbE;EЅpBɡp|eH 1N$ rQVlybV?ѺM|w V]ȑ#8y$6mڄ>h>+ 9Zx2uD6=n0%7ٗb~➛J ϵpBxIHL8YKb(W<^Ѷk߶!pp(r͝r٫W4\;kӶ|>໙)b/52vJ%_1$KZYՍA2T*¾p|}|ʲ͐,8::ҏa`T(F-h%^H!\lֆY4~&O{tԩ3C(a>4X>XW J{ۢ)YPI8^gCS'С8! ZuNݮl}m݊y0+qbH YPP3v,5.]j N6mwlߎs$G;w07~M`ԉXgK4m\/]i M,_-Ҥ+rVoAk^ @?۷x}\ *TPQQԕ9op-`߁~_tbμou4%߆[9ч/[+UqR ? 8!!B1HݵPZP  |S ŭG A ĝ~\r{{{K_;<3B?"9T,۱+9 3%%#eu_ʏxN]au3{M;x`,-`gWҒWPOaZYY$<GmV=8=<мEseˢ֞ƍ 669}JJ2Ξ=={`d0kL^ jD~}qqDG/>! 2 y*ߛq Kq]"((ӧOGU1gZGϞPt/^ě7op)mpժUCfHuymx16n܈f͚S {&݋B3[D*… ӧ(sXjjժ}cI$ړY,3$? Tl: '.% }\jA ƇF͉'@N^a|*&Uwoj Op 沖OԴ0H\5×v+MHA^x~(U>(O޳0mKϟ#sJ +/«]i4vr.. bhk 2q?z];N#>>7#:;wlع+)ڴs̝399p'O>}>_YRz; t@TXv5qO/`يX~,\~2ygl`+uv xlلlnAv7&R\sܿ=RSRb2ԨVYϙaYG(333*RN IIDf`U__? fhԨ><ԯ_)a-,б\-XZZb"՛㣖7Aعs;&N vvvhߞ|sgqҽ}3p T>}HOؽk'&Z:s4]Fʞ@Wgnho҂װhBJ.]rʢs([Ѝr9z&%''Wo߆a͚՜z(ĦItgR?g2puvFYOOquE>}p߷>|%lټrzQ|y4WujBeq:و 5Uѣ%ϟ?ciE5ЩCZRٳQ됓zVCY//١BrK(1t6i",z3hI P,?~Ǎ;\Q ]]ѫgO;{Ɲ۷)~:@FFN8ʕ#G@@)+6܏̙3ѴiSԨaeq… V˺-YB1y&uիO>xBeԇªPV-Qt;<:vF bq]:̞3ODD8^pvrwnv O©')H}/[@p) 0eQlYԫ[uj׆7Ν#/;::.x1 tr_ ˕f(ۡ2^&O _쿏_H - 2F3(M ? eK=!=oZ7oB"N;ΠU6* ;;;|͛#vz(_; Or4+V m>Q&pw@zF:>};wnck/}wʊl=9id۷qqyܻ۬w>5jiӦT2{ƍL2ԉlTcޚόaHA-[D޽iJIIn3f ko޼oK~ifYk7l@rJ Ν=;_iiiq=m3״)ԭKKiԬ`;ziYGgXjJ : 0}4Z{ JJi l=Xr%qqq222p5xyyw -L1P(~ԭWիߴ)roWWW>rݻu}RSSJ̘>䭞^~ KKKۿ|nnn6QI$[ZK0KT:LNAOfq{TTSr^eB#D}gR@cZXR'aՊ;{:6O+'YYY=8nne`iIu!T$&*V}p.Sb}Z +V|XrfϚ6ţGʍ1sDܹ}T(Xn#GBnUOpZ|b:meM4==W._¶w.kj*B_˂=:@oK}2yc0l`th M}5`u|EHM5$O}ZZY60}mH@9Qc\xw!'''aʔ?X[1337"733Wmf޽W\FJJ2B ɬ(Cr?22.^%0ʔKq1ҽ?.ZǏŨҿ(VC}E3>h]zJl۶U4 ͚\4 Lӧ/%KDs˂@~6>B0@&a8;A.]7+^J cggGqkׂjJlݲEgS_xdXzm؇dO>H߿4\t{l)+W`͚՘1c:V\ׯ'3g̤6&+M:+N#䊜  ֱխ_oDn(h(V:wES<|8?|>qQFx _KٙSfS\z={"ݗdع{7V+VZ+'ssUјfG@Z>5k"Mٓvam;u’<2t(ϝCpP^|uC6mPR%F9J•xB(xzzuVJRo؈DJ ;;{ԬXi'[\=N:ޣgغYY~ϟ=hݦ}x-脣'aI8sCPu9{>YeaѣW\/]$Yrԩ[#FFN]WUuSܮlll@xOŊaؿo֭Y>C9ԩ-Zk"St.JݳQH8ϊp |KLj0~ ?Ė1ױv+(*Yґvsԙc8;{YV?:*k׮Cr.Vqw@q@h9M_Ly]{`lXW)[[۠q_ qZjkgbbu7EVXz=|HPvA[#L(+31uLt ^ΑLLLS?<zaps+<4SQ(ܥ+:uׂq`>ѻ?[XZq& :wi=}wY]ǔ)p) z_F:]}%{~^C@5Ld5W#== Oy8hK@y@fVb~1p ̝3;wT1r5Ŭs-'3e2ٸ ))멃r${cܿw͛o_00-Pi>He$C}6?ʜ)и/7eMOkff tԙ}{fп?5~_}-n L.]* @Sl 9G2$иqcZX:vꄎo-]&r9uv:P }Я_?Cvd2~+|nÇcjXbhР4`r tMW YttS@97Yal|IBFW65L͹-/xҥl94Bl]0̊N'..>}DlL lmmQ&)8 ʸ{dIGr׸doE-[w('^1x ʕmDEEx-Tc0G|l,,-QJ )H 0fڹrP0u,rAZFtt#yc]aێ=hcx"kN$K&E]eggwBvVJ::ۻvT||<>X(QdIxxxrYYY }(XYYˋduе>bbbsbana{{{xyysv/#++ nnnpw@`*ߤDxPϟ!''gXh{,d:99 ϟ=GfV&JO!6h&ϕ]B/s;{6 י&HIIVY JeawdHHOOW/dTL]Op015L~X8Tyy))1w*a"֖*pAn`9'hԐ\-Z#ܞT!E;! !1tOU&&]Bjr v&CAA$ZeJRY/ >` فLbttЏ,1l%hCT7N?\Bǎ҆AI.C^ջ/6lܪoU 'ȹPhc̈l&HP(E|;m EmRHLkL*k۳a·N$1)I#zL9& oNSS3ӈSeS:7 c;QEt\BMshKQX|=B"[`A_gS#nֺ4$'SOL֭$5-1T0Hq1ln=Ҹ ܤFYoBѾ]oŸs4)Q][꾟~~o1!xg3F|mPڂFVЇ<#S~ e˵` 0~o[75T*_{MG؛7hߡˢbc{\xgN$X2zh0ro ps*>i`tcQDfA<睔TJEC "/jCafJŖ|"+ly%vJWU]oc{ :ZtrVI8T^[0F F;00ad,.ډs,&"h@f $&sE\|%+UƾG{-FߢtF 3.nQ&mOJY~(/SбzdfÐ!{YNFC\NɠoCԧv1(x5;̊ѣ6\`!?Q&JYK Q N_`ʹ%±N027"an8tF7y)QG_Jlʑk14tbߞo~#kJ,גG"PP 2SH /1OܢoWgƫIMDKcʕ#Ix+(ջg=Vqhjz,os~L r7i ́G6 #S(QvܸW._+&111\kkruEUШ/|aff@CFJ! lZ3: 8tDa0Zfoa$BxaX4A!T>#GavH|>TPi>ۣ o+|9}L0Jv| ,W @ 4r-[AVm9m D.9)Q0 $U ,ְ0XnE0$AY2:3:0`]US'G _ RhxL EA, B^%&φ!ty7 J=sRI=JłyK4:mfd2~\AZeH%@/Lq(+PBa1KLi`X< _| c'#tu2a׌S|41sÃ)ے8o Xd/JhYozv'/~ӊUpA,ǖ \|Ez7_z 7IV1oFHj|` o#O^0bp ~kCXqE%^h -ȻEf]2%{(ڳP՛;9't#A hBzI˝VfYy#- ˓6]χnG|)$&r%db*H` ՂtS8zӂT`< E:!6X@;)LQu*|(htcALY̶ynbd$|=14 dz *kF\d%11v^⌯|ЬAv N&1 ,=0=\aiÕ6 3}fo煡HV*,J@䪮@A ^F_Zd}k[PAo)'[S`\u,E(U-DWB#iFE!yCT1!z1hK'27,V<50|YIԣ$%P tota=>17.f{㣁,JoD ϛtF@_5B*]g_\ҦfhHF[!4 E0HRT6ΞFw|K!WCWF^jG[yfx/-xj`$sm!_f$"EnHr${ZT*'f~ :O Dm F1^pA.EwHus{%^nj{2q-XPAsM_ȇ7IEBH~'- ` P5|t&*Ч\ފ` y/lY$IAgp9vpEs4n菮5 KH "Ȗ{Q~/{8b1$/ho'}W8`2D{0Bnjr~dvGɷQp/:/@6C& I8׷`HKZϫɈ T}F MxuqTNt&WSIzZ=f:hP N7,i&1KVF56rUj$(?A [VDzKi;7nb0L=2=q.A@n2tʷ$[JI^Ieh1~ 9B+^P&goBS; }SoċCh RRx&h2TNNHa_}e"*ܷbuX*N.}43l'$I%%o9/F.a^;)>Z8FY"{)![>d ?}aV5YuQ  CN+pH[FO9/{Xb~CZ%i( EԫW`_nja^ || .]$9 a}Ҙ`^g]NZ)Hg8A ^+}:_R⢨'\KJw>Ia)E*Cje@XRF[<{YAV]x( d@och cͺC?"ykbQ[[ 4)WU&YvA73VOl=z ..p-8>@E+3bɟqFXX;ֽ; )c]т]TGcE)Ε׫wzF^(n5%DcSN/W􋻸f=agnŘb,9| ! F(dKLHmjˬ{YYKb{]Ξ3SDp(kU Ie(DÔ(|WuY7BhԭX"[␒%Jʊf1PEѬlDGGDf1HQQF {{Q!hDC AF62¾jP;*a_}|)VB k9,<<E\%]>ZL8Q!- Ȗ͕Κ07zc1j ))(UVpB3lSy.'hW[GB Qʜo~%":O<|ѪyC{QZuj"+N'ʽߡO%}l򌜞}{wԉcu3iiy+^h٪-<eůKivu޹ T _,XvPUlXA5`Pqc? <:v&GPؿw7߃##=PX1ԬO(V^;!;y{6 yڶ먾5C JS]F3Iq~@ +ܽPN|bħb8Pf #}alAPŸ1!J)N=vSS :JP}PXrǢ/D:j4˵HG )ڤ)IXLM:jHH26OĄFR#}CNe7hxC@Ӱ`%hY;Vܔ}EA\h-qaHcGGL:OE?@O3/ݾLV]fLl4޿{ @II$,iL0ϖ;qfNhY6aEYP|<1n pfބä ddd`cgvt\|W._ؼull/Hg\ 6Jgff}&vl߂]{ŅQVPUde1ʆ:! x? bTA≊ =A/1^:]d9o!9Vҥj `ccA? MҦ FLX5:X`HWr\( l,H׶ RϋMXQɣ65”7vz&rXrGow^ R$"f5KۚF^Nv.{D`6@6 CX QYEOOCgqX/EBF'WctG+.@0G}Ю}Gxzy2޽ }qQddd`ʤptvAN]YČixUd~u ߠƎϟb֍8v^:K.;0~(dee} DVY.6mX{߷;] \. ''wǏBBBJvCt_֊*]*`l \ W`z Au.a"X1D>)ϨYB2c"jJQ>톃 ח;FV•-)d03-L@I+aƵ5dun s!=wА`iͼET.anZ i 4KXZÄݝ(wOdxϵ*W'$)FЭGu(4Ec 6m6RnBm1OtK$}.4;4`CY%::u-֣D {,]K/ZjG?;u5UVmM{6ΟQݺš [H" ac_ԩ[S'OwxӟVޒ+7X2{vضع u r pd"A_eQs= , nKd ?ݡPM1>373>> RP((RJ}?''7o@WHHH@IǒhР!)24$RU%/GeW^{ANNQvm-ƽy&^z/^ժWG ^|WBUaÆNb'Ǐ~_|L&4j2eʨK!4dpFDhAtԸ&8P*yZqJZGçD|^+e޵6Y˺H^aӂ/|-J;Y p-8 /^Dڨ#k|Ţ+JVf&3Yss ΰl@gORȵv~23 FFZWA啧5xڊp!33Y,)MA%HNN*&BA0 k*sy2 N'FHH0<<П`҉#8pllY=~Jܿw_ė/ƬǏ Z=@k KcL0V&?jn~"ғ,hpYz|nXR<* aWN0$n)aC~Mz5k5ރ脨XXsPn]̍6`ɟUq90p,?޾%Х[7bc6|@NyF8mFFvlۆ wrרӦys aՊذn(J.'Lcz3gnOO,XVyP} Q{ ,7ׯ_ ոI̝ժW#wIr$>HP]=a]:2DtADRMa 8Xi$L&BVϕWܜKZH)`eO05܊4Pm”cr$hJc(ͣ ~v×/y_QX<uzG'j]r,*Uc~3vl'0s 9j׭)桺O-zD͐`ݵ!9"`mmS^ѫ?yh^!~ػs3FuѹkO@zzLMM]-[ŰGkiF/;酩3¹Ә?g:^z++kw6~}Xh޿{ [ihLc ”x4-_檯Ϝ:/# NY#8wYܿw\O,?K}}Q<{@Rb ׯh [;v$>yii_o.5S/&C#*WGh+QZBgcCtC/B.Z&mCzZՇH4cpR'dp|W$"_ʉ%|7W<2&ytT0a8ݽP(/_{>4l۹S9`>~ B#6&>D=0_0w|Z |}Ç^(Q"O>at"6l sE^2 {=w\+Y(UGE__ml,\@=|=d^zaaZa-hӶE;H|x_<x&"[֖%,}PfrSddgAdK&ȳ63Wi9h(46P%_2;KA+8b򴖸x%Ο{,eG[&5;Sx1N83^IbfMtR4\DB\: ϟ{>~Zaa8y\Jn:z799 ςwHOOØ_*11KGq;;4|4krՕ7&]߹}Euy'FP~h Az([P_߽qmԭ׀ m0 n|\om7m:T2\ _pQCߟ6:T6*Ux^xk*'$\B Vo$7"D`3l444s#SGKdHy#oƐuQ/#W{"̽aCӨU6&O&~1yӸ{jשCIӦ.IIIh秕4͐/_[jjիcҔ(",;wn߆IZZ&0hy \갷o&m(lشI-ISnjj*&0իjՕVٸz2̚\KW.]xzya?)AAecO?! (H_ LE֊lԄ3qY!Ѓ5ڒɽ “HV &'KZЮʕAxb,u $+~*ws~ƺZ8A&19/KP LMh׾2j(}{!,,F,11 7UKOZc<S 0}8lߢt,)-pv|jR?_j֪ACçf)-އ{w'ݭ ^ mq benHh=#6D$ǣkfxKԨY? ZuPp~۽ /]ܕ][wmu5s)dd;زi->Ggl~V m+VѿMDx==;0p$fRBthϟ*S ? B0Ǐp~ݵ^>GέqM;#D1ݺywo{iщ~Kԯ232Bi2AKص}30mL΢"`Q(Vc$,7iX/kİGٳ'Xz9+yPp|B/HV;#@C`@Nv6TRX_SyM}|jp-ek1Ď 4%U7@y/P ŚUL{c ny`4LE`c7 y}CZ/#PPdHFʆGBQO] ]sL(2k{8{4~g̚;WMLзDGEa\~ݧODsD`@3m;OVyW|L}Թ ֮_3=Md&hԨ1N>w k׬Q[sm[oxԭW'ϜAmS9|z@6m(ڮ^^)碍4`PChѲap)sט3s&lr8sJN__ըI-^] T/Q^eѢu{L>kW.E}X3j9zLKѭ;7h fӐ> LÒXˑ (4m 1Es$!?VzҍabŊaպԥ;)Lm0`P_|-@ 4oM{@a؛PضO?Dw;|}\ !>6v%J0X0U~p9=%PV]IMo޼ kcF@S6s.G ܍NNΈw~L璭qHOO '':kTԶ]u=oLdffB8h 5ŀMOK|Ht&A(曰`mX1hOCL1Q0sxkjӧz_#ܻ{Μ+W֭4!ѯgXZciie+V0n3dcc/G-+;$۸aLj s5mGb2`%Rj໊ѳW/ڹ'Dz+hozTyxv+(eSVĀIIBE'7ybb.p76㇘$Tr)ϵ,( ?)[_H<&A>2 ik%MwފTbb: oD<&d24-5J'}Yjj&nx>E[U 0H̙J::aۮyDOW/GՋN]{bY;t Oؿg;&MkknSoqp W.) a9w솮O=8w'L [ZojKHNΝBT3''?~{ iii gP\8(i Sg.` ߺ]G3lǡ{0m<NWl$+[^cM*|W2=AX0w:1H.+WQ:}lsծ>2ȋw1)tKW cr=^` Ikz QQxJ/a豂Zl &~-; Jc6Tޓ1Ѹ߼ekݽwBpU4!!!F EpU8>AWpVxsr1=܆&QxN89j Jp'iN%f TiwQwt 5${DϺa @poڶo*k)"++ 䰄kS6N;ر07W 5Ӻu&sOkع3fVڨV:?z{!:* %˽sZ^QtiL2ʤE%(ϟ= ZoAjvׯ_Ԫ]`6AsVjWlߕ, ôpEC:+\90%<~@IW 1O @:sEw銙cJIܵ+5LCB[_&ū7@5Dliu:Zߜq?J %mٿs1qdH ۔d2L"lЫ ۳6; K[C!7998zp0 IDAT?J6h|r-=QJa+T$.Z+ʗ#sILLE,MACc1Zn44c~.!?H{PEz L HP!G^*_ (**=Ԏh/৑A#?g A 02Rp欣#1<ȷrbxyIү~oBydfd ,5^|o^#2 ?B& =?Ǝ@vv6|ѥ[o4h OܙTM|^QZŖP}YPժu {Zo}hZ$|Qz U*i>#lqdCr"зi3ʑ'}¥+Θ n֧\XbƎDu$&M 2D̄ Zׯ^nW!~w}.s&fN køO2Cjj б ,-+` uM51zBqƀ| +!BF|,i'I6H#Q3NamlPdK=**JŅ#Rk5\ԗHRFG=&P  ;+yc>CV=.0w_ӗ#9ҙYtOA6(:}Y9?}9M9!'DK%A>dB6 V`ԯWWJؒڑRRw)e/qKdeyJ?t䋐8]{eN$P;CY萔K]aW%&#)Qj T TڞUu+1!pVI*dgg3~P{̙9I&Joo&XݽSRS @yvMLoVUfF45V>W"/*T a*>'1dZ?ЩئmiA}Xx߻_G/1elB 9w޳WK+Wb=1fD8;`#(W(ϟ%]*| 腌 xxza͆͐ tW HMUժM{ĵ!cDꑥYYu$s WH4twI`@E!:wrm0 kk Ƙt$PiGGBhe2)"J$ //\Ei#S#|}\^`[ / 쭓[)ԃb'B014 w!u\%&xh%\r[^rT  aؿ>>& $ yKPw+d3^xaA6N c21)%ӏPZz)֬KM\;i(W"xx oTi@[} Aq`\pwDVf&"?aؿg;LMMѶC1efRe h0=O#RtVlynJMQ"e5p;)D? 6gNA&hڬ8*,(:Zu}ii_  wwOrבਜ਼ʿz=CLt4qI899k (ٳOLMMTB<'+MXAP_'hEKx~f*CFTrQ#w쐟ͯ D̀<) T1tm~uC?c[BJtk",Y>~Drr2bc4A,?GDЦ-3B#3)m9[tT4}?ptrRǟKwB9*S r0)f[#M$\cs|d(me[s+̐\Q'&N_Jw<U'5%g8~zereٖmI8n7bBM\>BpoBM $B74\pݲ%7uKGg?N2}gϑ^uf}g݋+mY7>i@ b1sr/59{>ϥmUP!1k.~/t݅ 3B{0{:8͞r>/}`UXz֯]{v7^_\g ʈ؛5gc;rb$(v 0 ɛ/9Zc*/dQxK`? my L :',n@JJuNy`d}Z,nJ9mt|e˰ȰԩS#ׯ][nDnNHH@IieL?>RvmXlW 81vX 0MMMXre.+Hd+4s.TCQyZi9Eʌ -BJjffpwƌ%oG>}^8-7csO =󑐐@ ,#>jʴ3|]P$\~x_BEy9ym\}5[mFls0J91H0-.H-P P-C#RVExUs%˝8ތ=`9B\IIBw Fm…=V3D$A5 W]u k׬@8gIC?pk"] j94<"&N8XS^|Q҉5X#Rfdo>s~,~oV>uM]g̸ QXf5Ad`|-} ك3HEmj ?R ߟ3zASc#59DGgp /y<n%"10 OdggE׫jq͕ qq 0/6f̘-f$ڻgw{QHv>AZg;IC#&M hjl?"rm7ߌ{D嚵u&.}b@ah{1sl~0+::AP9|HNNWNƃ]*iD5rd{-Pd";8"P) K+ȋT,`rJHH@FzGO)ƃa`B?1) y#'5qח#>oYL> Z*~_Wu\D1c޵CZ[nՉ_{sɫع#{u;1`Nc?4 'II \Ǵ<}<O o2Ϭ^FKvs+O#|kܰȤFHH }˟ w_HN/-},*464`Ȑxpg'%PEps< bMR5(v(?2^Yb6v>'Rɋb0]uutgރ}>zz0 /߿pEa֭6L !W͎|&yI >W^U_?]ݑߋukඛo=wߍo= Ҿaڹ{.-7-ƾ}{#֬\y pɥUmO>TmmHx?G{eNE?K.ƓO hX2GQmhYݳ [n}lzk;PA.} wY4sؼyW?7q0r7~G㏿-Ecԭ^zކ#;RIӇUh|><_+|mI٘nksԉxQGR_G\MP  "#q;Ǝ]w܎E6e2ƍoI({ra(**Kѣ ;w܂c`ٸp\L7]{-!E(55ϿΝ>jJ\0w.--%^qעlpmpEs Si9xg?l3ɓSqni)-)矧1]_gHIIA{{;~c<~͚,2L?Ο<<{ q sS(hH4~&wXO3b>M1 wsC˵ESAҪ>MKKWOƼc$,_cÄ~5UXضe#.? _zfY|{:v׮#11JX۷ӱ+0{# >{zݍ_n~K,eff_hm9k.߂sNGZz:kkqZ|%)+˸Ԅ_kG456b˰jE(LHH/~{N%hc%]yyo q-GI4ttcxXf;r#0k<,=|;83c 7>G}ۖH|QL~QtUf SC}=/υ}q ·b֬cx/qՋ0ir1Q~[* ghL;{Ds[Do?6Ze`\Uo0^Xhmlbm~ԑbʭi.1& WWbvOagTibJB 5jz)F NLm'ΜG(~r|jZ+k_ú5k >oqƬYx'?AqI)1(jvWSO:R\>ӟܳϢ8PV)so}+\>!CŸz OhGMu5j#Fo~[X_`j/uFGۋc瀧bM7ᇏ<~" O|˘3w.~_c; e&{q+_fΜ*[x$&g]De a .lCt>G]૫4dre!ۇwL&=^dgA.߄ g- ^_O-Zgͽ(.Iv@%: z{{7\fΞ(9|ノoE ½|+#ߞ} ͘,#_SOVw)//??.#@ 'mD3f)N,GۅHn|{/3]v5M8JSRZ8(2V=.7 %ꛬU31i@ HH://r;>|7uϽcካ>y Zkƚȑ~yqp0q20?3֬'נv6ƌ%_ˮYĚU`n1y9\U+aըJsr0qR1\x .Z z hnnm% IDATOV}#܄:,͘˯Ƚ?06nX1c &N긤V,Ò_P 1Xp%蟝M$0;Ks466 +?ƌ\96o\e7-;>:ܵs{|<$rcK0@ 2 K~Gww 0p .Z\Rчv*]?8F Kww>Y4 ĤŘE#fчU9j4~(.9[4|W2oƤXs,ye^'נCiq5yDt'sl|BsAeQVSO; Īd%[^Ќ#0 3n f 0DŽIns9'sՇ X1 tALw@Miq9u&·^R?n0xi'2 x30L=aA_,u &1A}y->'ȵn,.|+Gc%='8dy0G1sSLc;zЬd@Йbp*YjK`S60IѺ+VG902<1DxAL31 gt)&ȳXgupDcƘ”glrIE7NB:1!T\OsRA _񦬋$Qu1p?!vq(B^z?:QXRa9ʫ1șgT`Wg2 x>H{j˞\1s}J^Pdĸ#ՙO(/Z.=j6gO2?q$;ŜYt $qGH/>G^C0KV+ud$ɏA%K#MdXH/~r |2H:&jBy#0G-tqš᠔.E(F{r.zԉ=dDrWqi]_hF_e Mdc "T`'u`59,=K!)8$Ixu;YЗڙFg^}e])~Mj ׬RNFE^p7&o~"'g{l0߬8v)69ffFk:@tԅ-\0W%\ 4ۙ}ӀB-įҤJ4Z8CHF?qRI8dt誳Nv4]W5I(O3H?0Ly*)W2c t QQ*RBZE>#4Re! ^=+ƂڳEq}7LR^H2ܬz81pjiB T\U]ݰD@3>XH5pP}d H(LHaL ,)^y4P]BBA6ޠвѰ%RIY0rzƙ)d,ٽ=AxęnS/i__`8J;l~v.\ic,:eHr_}&J'MjIYm2@MpYE!șC]V=8)t 1ex dYķ `y' TR&إkbfB1S`Nr#BR`[ʓ+DJ#!aYI(˹p| -BH(`|ԿX7Q2Abf-h'fdse T8Jh$<-o7G[iE3cAgۢPqɰ 99N+8 1s7lߘ@ OtLlQL"9| n3M E"x{36}Qڂ*`,&Zm8LL7Hf'D,Mc0@eHZxD=2tР'$SESE#9K8zh:o"nJ #/2PU r%C^c5!ev3M5l΋A#j_i1$lKe<^1 P֓)'wSQ0ewf#T=z;̋Β;|Z@S &fD7 J faJ_hwݼ.b6@ր*90[+ݽAPȪ\ d3#vF;J8o*Hچ=y aH/ dWV GQum)4LN=@z2Eb19Ӕ.b ySAMOU"9E 7PϽ B0?4+ʢ-$@M,G~(X+I2ϳ/X/į0tz$J A9FdM ghcc "M8++Xcc|FO_DPT9BTz "rP˸NsxK_i@SBDӛ_k1${y9E܉sjg2\t$ IB UEU ,U\U |se$WmBXJ` ƅ:v,Gߘo<~+\ t_DOhईvZl Da9/)&ρM,"&ֱ\.<3b#G i[z@gn1KL:IQ x%e~f)hA+76"'6?mMpflо>58%RAM.+2i89l؊h# т *I'Lռ9 >=ks_C!RAFq}8]T}AZ iP ruLTs$ [(|֧g=`#uE-D(7 yTpf~-lHcP&9L^L"x13>)0e%Ob9qC1b1I*X`uB6UdӕPߧxq0-Ed >p+BA[w9 ?M4+^ 1~2qq5Jz~[ ȃ4h D\$-%W>T%BKNطU <)Y&NWG <K8¼,2l\$ Q`=dQ}7!)b24.AX\4ϱOWa}>0%i@"$m \Xeg3,8ٰX0/ @&C1\lo؊Ȍ="0HIOTjܟ)?Kyg[KNN嫢&MY 1և/\ 6L\|(%*r&~QӪ(b\zV u';1yIR>OsNNvMyaU$.,EĐ O(S`مy9EAO%\.)xбrK6" b%sBa5Rts/>9#u$z<&B+A`'=]LЄe03}w=baĹ$ ,>2ooId \1 d..jdW.Fpɚ7`&^?Xg|}Ȍj\-l[-7RkkI~g优ӆccؿXd6᫄ԛ|v<ڎ@@BbƜ#*:{PʷӋ܊D)ܬ]uY-VV"-Bl|>{zqhg ֽu8$:Nw"k`SȮ lS%oĂSy1l_Q=k`dǫ+o1f^LY90umر'6b̓Zv߮D CP:\wHv=N?no>찴Uǖv娫id <<>(RVpY󹾸KuyRgqhŤ]a&E=Sª ʩp|]Q3˗|P\Ko L>6+B6 k@zT̽e*Rߎ1| ʜb 4ն)LLtl#6ssyוZ@i{oĎUppk:i Py[7`ξf p|>_d~zcoce9lUzX7^0g\> 8I o͕tM4lT2S\'M>al0>DbU} GV ʢK 8-h v8WT1:Uq?ݽUO#Fk/2dE (3BK<[^*C?:Ew_I)MІ^IXw#=a^r~Wnb> TV!u:t<3gXrp>d pd7Nus& AnI5JzV,sh$QĘHvzV {pJc,ǒĜES=8"ԡMTOG.]3! IDATgcS,o?OH_6\!11>CF m J^X< daP^ML7fPOq+P| /0hº'  cp~6`k; YvuG<4`F_qyfj$; b`,f&|z .iRDٷYk; ZL2r8yŝE~DXU?sX1"DZI?壳W>2L}X Tu&he>,-w,/Y~t|]U6($&9tRuF%m)$s'L#K9g0ǸvPP/Њ[K1a3Fn`eh ``8HrqӐ@xvݘc*e-uLzxq;j@bRa-V0ڃϻ,W(;cnsPTg{`sUpG7AsG6Wϯ@Puûa>Y{Q)H5~^_͵D~T Ymޢ?֦_T =]A~ψsMnc3 `APt,\xT. )M7 X#),JIdm%l@/v<+\\N4\)]Y"삼hI䅽ڟ jLI\LNME:MUχa}!9o0}}7־{qeUּk^ۍumQLILn;@G`ر23q %FOW/U}հc%/.C"@HHL@a0@9)#ˠمHHLiS%ʗwb;re•Oa;h8B,Ygi 6IAH?^/ #& 2nKn&>XyԹCRJ<$nJh JRS@.@tВ'NDLX,UHa|EiV?4djK2\9̇$?PTZ 1c麈֟t Tb%aa UT+D"rEj c:IHGWTb"rCh Ψ}co=(r*#}"b}e42ŭʊåe |Hڷ)gm>NKӣtNk&?[\;8u 0ph? ?Z*OWz vAHH ^*Ri=e(`ך }zF@\cHMO~XcD/ P{ ;Q>%- t@m,i80%{g}eKV2r^d@ ObՇ  LJh~!"!GQqYlh4Tg_3̄մB, FZX{`%/b[2l oS,#KV)ZSRVj (Qi3*E{tI֫/uSgx<{I1Ws~Hf7:Tâ3WEKcW |MTpZ˩SŶμX:ۻ鹂Bz>LdtuȾ˅ KG:1!+J{_ɜB@Z4j6v$N=]~n =3\D v;vK͜aru{\ D v= m]rT]5 ك2:j[pl GgfdlAq S@s6yF'i/h h&$STI;f0Lp݁ih; &)T@- bt;Qtʐܟ8L"|'&8345wdsq(EE`s`+ $e%P{1>1_Lot\IWAƍ[7}W&nb=$ !IW[i"(VBPin;kRH94?Y&V >;~S3Yy1E5*у ~xGmk X.`Il(x{1hէ#e#N׵!-#|u6aB{SthWavd߂gʷW#ZtNDQio/0 Kh7w2{z/8 Snz $$Z[T_;WH9χcb)?Зr3مH&o؞kMrZw14~A԰W6>&-Y:e+`~8Td>td#tB%>b0gEqH-~a[s'Kho~CF""5 Gu“:ʲ[:i{iBEZ|`=:ɫ񪓑W6%Z)Bζ`p/|קȂHr*Ԋ}/Т\۩MM+ SrjL 061k/ZB /s7HH/͈*,L&35=Po4DFH.[T2,򢣲tމI4{ZC\iB)ir6V7vu֪h3, uʙm8|Quʶ'E뇩 CoOFks'fbH@'0 T8V+Rnt?6:rgv0b_R#x DР$$QPAL-"WҨvwJYcu+<ߙ?YUt쌡׸Ck#i,dc* LjA#|$^$#al(AIZlݚJw{c.M;KbSTEH)$ONhfN-m^ O;U >i~:;~r0MM )6Ue+NZ71wEyX3Vvg`ڣh>Նz6qM`|Y1l?'Hu(sPCkIFbԔ=H"nZ>R3S?v*jqpK%.e W{/+SmRߎ{NE ՈT3q wn1~Q\F]~iS:D"txME=ʶT!9% oyE  Yov|u a9I,-tARJbjFV*hd֎d*?/)Ͻ,SG8B7c(-Y\ Z1=LlN@kaax~ Ƃ[2h:Ն 00~KafiY)3݂_􆞵?7':_:3hD6*jv e9iHp !bpV6a-h 9-Z?L A ysp7 k`΄Nyw/>Y+btn2SlG Y}އ2k51 TY,- :]IpǑCL28DfOJkӳge  ot~4W*NV[xF wZ3 ܴ1 ފͨj#? O݄ \as1g)_[Ch<4vpN`NQy! rz?0+tvxK Bx{ yDGj]K:3%5wR5 Gʚ|φ7|k5..F\'rky"0l.vX3n ;z?7>))I l Vztw#)%%CQnML'DAÚ5`p&'k`z{]U]M\Ǡ+8)zn%ha!5Q<{>wnBWGL"k[+=-#|"͝)e ӝ3)ɸ; :˥%#!.lF~; crÔcumesa%uQ# 4p$m!\pz[S% ,G,(QiɝYء5ͺO{ rR}B3T*qyp{=֘;896Љpj7|1,V,~Pv߹@g~8iB]GvP׾tf`tfњs u]S YoI2EnQP}~A0E})n8RUؤV(AP}KLG'(ӏCێckkVu94O_Hf,yb5:zEAƀ(Hb]}.jw/>|~ Mk mZ?tA.2Cjf ںqh#zzzlCf޲jo [qѭӐh͐?ihze=ط.(rJ[&NN7i_DX5l 琘9hoCÉӖ旛j"+'MumHJI$vca[1,+Q++T(Agb^as3T .j;|E6A3LKKR,B mmoK(BU`fjAvqউW Km1L zM^7 }zdH"6k9I+h j'lukʩ/:`5pP چnY={pgcL';%$![ =~ NCrrLϲX>#b[ (Z3F:C#VEOR@Vk2 #' 3 ~6,ݏ.Tb䡰%K24۪ga(ԟ)5}, v=ފ=]~ 2r ̐\߆zNFvȭC -/EՁZ?.A$^Z`S" LIE䴳`#ZRfAÐEɰ6f..Ug.DފD}9)- t` $eĉbn?݅˘Kʠ&vϦˁp;ñB3obM{`P`)^G& ˵8hwYFPk?.M{~Տ֧}QZqr3 ֟8s$*B:k ś D: ܭװ$39PwZKŢ wD%~rEw롊5x??âl9 Ơb[ `}7=I p0Hہ-[SҒPtp$&%A+NB 5D~Q(Vz4oAN^?D{Y$tuz{@'mVtpAKcNi@^a=&1S#:)۪܁0|L|Κ[̥ ' ņ@jL9hGo1T[:2 Tp4/&kʒ/'( -T?\ KYu 0}C;]כ[0 */SLgֽB!~ Kϲ3SQpd ]nػ(`, E-)H_ְ"0?7雴;)~Z_Aa28uEX+AJEO< 5%_2ilkC.+*[t Up`;cܹiDed)#u{sl2~M̅ i/zvSPw<}uNSHQSkij8@omGMwGh;e21VYVΉ01kjM>LpZȼ 56|k5úmav>=d G,\lOfEiK>I)Alo[mvP^XNlZZfa] @itL:M  Ҵ֏ }ILLY{Ȏq+ ح{k/A P/z~'g_{dOlՇL:D{͚7wP`5So\F4|A~[cE7v<)H | >̿p #v{xuj]= Nb@}'av7JD/Aޮm؏ mt۝x24L,ByڀK`[*BrGW dB7So9r=T񓺺žX3 l~՘IϽu{ /oA:^"7 =FosGFZ1%{ |LNpiNҖ&&g#n󚟩n^sCVyigWV,:/B%}ʆ0H- /1/KaDnXD2n;?a+dؾahlYLQ= 5k?>Vi8)mDK] GjF2Z;qps"3; 8y#AxTߍoǦwL|> î W/zDd` f%3g>^]{;j0np 5i)hm'Q @Bb[X%L.{|l h-t eżx(l,ߞ(*Ft^vrl]v-p~ҳ؎M88ҳRq=3ړڄًqhq?ۈ(Y0T ./M# ƴl5)Qx>Ҁ/'uز@d1}x"0-Bp[?:ڪf̼b" /)U~~!~xX2 %,:$qv܆ϟ4g ;^bY"%I Zö* gro9WJ  EXж( B IDATwN&IT2!0Ҽyh@K`DnANW  ذt;M࿔*`Cඏ"`~RP)e& Wd'M던¤ ty'.A`4@Lt.9vsS ,&*>-Ø%Ykv$GKU kߩkhhx0/@[w7Z: of"@2}g? "q@fl#\. Y%8{zGFV0b($)jrTz:z?Xt($[(ovvB?i'ng8v4ʬ@G[wߘv5:ZH0v tu!#+5O+Z8}WG]~ddc [HF!:8>JѪ:W%G[_MF&ߤ$xqrI6#/yaE}벁Yp*_:D&CzcD 9̶|b7W$iaC2Zʓ[J??=WPNWa( P?I/WaFQn8.P; wk\KwWDJTH=*b&_~JɋQ:6,,)sP]\):''2GK7DfOA.뫬,q2LJN@fT|$AXE=i!a崍U =1''#_*g^`6ll/Ϧeb98Yw&&' +;-xM]wIL MH f~>deG!%5=&f )YT5{#Rȅ QI% Дz V"+ZH/}~A/.pO$>BO(h݁r!R;&D^;6w-ISU1](qKnNjak~*✊yaFBr M/95z)eJe!/ܐ.ߘ1p-/f(衃s項43B 1ZwsɔXfvBṭXQ9g0  fGz(Ʈ)5tq#Ȗ![=ONf-EWg#c'S8҈ڪ&yC)Ob#9*!_,ZnogqLk#NLaR3> ޴c\/QDYH2@ct%9I}@ ?Y0u)1DKvo:E_{+p#8jYCuTnLVHg6]d*1%SRA±@P5R؋N 'hi7J_pNDh1a]8BT .ktpw=T+\gR;\ L}0/i9WS"Ol:HTչD?o5J eKu"X(^d.@TL`rUDz#~;CNm{̧B}ǷD!T/[o:<)/vjtqP;auܠr0I{rmJIlM? Zb+x&]r;d+I ̖Fj"K Sr룼nH.PӑgnOޤb(?70:YuLP}XįrD->U$#QYDNݓ^22T_% U)3$갛1|OCh|&ig8)-Fؾ3q{-gX*r\`jҦT&V>ӺPI3U;Ci5Pʵ 3~wCH|l WAjifcP 3REq 50/rd[p.iJuEn ; K#σT_ GV+u+\ubBzHhX5I6|Q3xe( R] N(Ò픬0Qv\^Fg+Hp:VȕBﬓ}˛ hb@RQ€ʼd Lȡ6| ӓEL¹m* 紼䄓õŁwEJ4mR,/["jK< dܪjo%C3j2çyr7Kړ&h~r8 T%v.|O1_y8Q1='6A4qJ~ɸ_q4?|LIU IʫQj|@`,MOs58vzQ# q#q!;V 6N]g2Ys0v~2;:=NdVʌNII}ɲHt2ih_1 gE^q;` ~$NL6A CZ r"a@y'^0ƒ5~e(RX]1e.dٳI֩@t$R^yȘH vL0I EMr˄|B~Doa18М@M2%:ͲO@ $ *Uv0-¨crdAd]:sah.qh O*H ';8PncL؋S4-_ @f%2 Ǭvd\(#1A |6r6*: %nxe"ݞ&2ĤuQ40q`W"*VE0urGY&9vq;gLXHgdtJ<)X%JFQd͏MjF2Em0VFʸrB½l{#(f; :'4YU?&䮇7 d0Pq Ɩdnfy6# 1 ]XIIf1ZUu&,A>KJ0=,EXN rH^1j!V%pBmcKqLqA2@FV]f gN8[{S&(-nN@戬ښ,J| $Q娞9*KX``bTL=1LpzO% ) <^Fk+OXsZb=DnXiIpݶ8ݔ~K'}V;3QO"H;FV7y]dLڭqiҐG$IZDNDbTZs; e|RIU  ;*ljgǪZ@cV Ϣ@Me1x()dS>ICgzlgg˜%@XjI176\bwTwdp0;*<*Ǯ䌡ZfifJ'2"%A [;,p$D&:<Yee*Jjd9 ,Bcug''e\dS1 $+QP+&` 'L$P șˈgtPnO۝ & 8b('f#7ݱfM(Ko*|"܎PohbV&-yLT&2V(9^) nfYc.g r~V;0V1?cgW;zlx谒+1"t; 8d)J &! M%6١βQNZH";; >3Ś] StL7ʣ 9 0H Yd,:A۰ʒQHfTa eOgBuƑSBW 6bC|1U'xB)'UhbfBDX;0d> D쇶mƏ[x|99 Q=Y:kI:FӶTz\߫1*ьzvFv%~*zVX!'23͒4T#% PmA[Y]r#+lNWd&?P8ԼS"V,i@M FqO 4dIfP"ifcc(SY ?\XȔ\eNJ{fE=H'#Lc~(/.6)XN?GwHͤR&;1Ce\+dl`3Y> ɫOU!Դx7B1/v-ڱHCf3NQcKRA$<;'D=_rù] ֋xM*Tl|mco"qaMcSG{"w T:ծz:HL{(Ϭs=3af<5Vw#q %;ApN@POJI!/?P,e4 •N~-ck9Hag zD't\+k!94IOXEcQ/$ch-TPXLwyR&c deۋ}fFîXqX!L!ik@ ]C@)Gjp6;n R1wk\Vju67MֶFB1zYޡbHĖI'x~ɈE'V7'QVR QS"bX&0%_,Qx!5xV6:)i>=Q%dYO53vaO #i.;ˎN%qr8ֺ%:e U7y׭:]۫c*pJXNehL˳~HR\ uo8OQ](3ڍU?ZBAd$ Y&1n eC#hl8`0HOxUndTKQ(e%?k*OKRUwNDO*m^i1)'OrXvpɜ,J 3ɽ - Gn"x:[mL ]nc2L~ j^^o1 ]lO`e<}&N5I̡O9s)?ίY&Z.,x@ L | Ve;B0_: 3UXm8񘯋lL iG\ "T+]o36+n9{b=uy}^&XxT,d*W>Oj6ce\ҬJI8r2Fo3d 99/%! yiMroÿYB p@bc-N$4’ "%ssu_7oYI]]`zHO? ?Q3qgxy)EDz 3?뮿[GRgcR@zY~Gpd:SMXHx1r,dX뭸Tپqd0K*}Aگx~ WU F0i2 W,g &6YZ1~wJ~tgs]vR1x_z3VcDFT>'RLd*f%y]$W/ʓ朮DG) |<'.sMjbNF559pya$qa@@<^Ob)^#pxSf@Lg\*dX^3)\[ȇ=3at!DzNrep-!t#DFN*rePs`e^^>U܋<2r5;MUHF tK"8٩b,!`Rev}x 6Vcgh5o%Āe0$"Z3GANa 2Lس1km=4 φccJ"jCo<^Y)dW+19?se$GJ,# 4tb9 #M4%A()F1!t&08hy&|1n=yh4|-}###݈F8cL1ONyMYf[f23o.L6G4M+`w6oWq#x~MnD(,vp/P"U8sʠFSyv7C[^1r+aGsc w1O'R 4fr"BmoݏP0{x|o5{\/cfIlȴ XɊq1OȄsuL\3^ؿIImNu:;Lp uF` .M_#ԋɷ]fU?{\҇ͯG&DQ={Jag&!53"kbZ}bțP9 /cN@&Al} `oДU0 HLa7wa* r(9O2<1&r[P{&lPv{W Y9|!QP lJ/0{xx"9{W[]J1BX]}XNVp(?OIaqΕs&6 FM5~zAFy7 ϟce<&=1y-[u֭z{wmA[K#rr1aL̚ @RMuInBm@ LՁSWoE#jY&Ϛ(dy0/-c+&?*ϯWW5￉3Jec< HdN~7ގѣZ3 Pi{3% 2&dIzGC{ $Zp2/6q /Lt/&^<)W]z=_LHhMrHMH;F'Md~xbۈQ,7;l>^Njصb ^?zƤY@NYƛq>Jtr&!-r< $'I S^U()X,"o mjV5]VpoxE 3֏#kQ\g-JTbՌ XճjjjW+݆pP #8ƾUܲ%٠=f_34?!ػw5[#DQ}:ilʿmpqmw5`T|pZׅ=GAzv*0Ba4Uwlơ-'pπ5 PJ`D6zCu r1>{,+U`<7b CF=9sGs/rW~6R;n|BU獨;ڦ?e%Mx+ a|5%(V?16}ш{SbGs/V> 7U,1#AKɎ;?S-V.|maL?(C 0;,1syⅆZxo) xW}V4B7$ d8yD),":ɒD !*NUU<ܼ`)~,Z1+Ka*l{ x㏛pKz#HMcS1~~|~/!Ys[8AF|EZd.=G8u+QKa( EQ؃{q Z w ?lD_>|z2=N[ [:[N x͸EԾzx ־kg8@g fC0^}h#ֶW+( &|J R3SFPs^ك;Ҋ7 Bxo|m糖ǣS;;;k #%͇eW%cC` ×v'.Ff^epxgVqX;险è>،5/BݑVm+;[21d"x!% WT|.Lh;/מe+po-w 7}ʯ +;g&|ҨcFƬ?)"3Y^~6GO4LVd/:,Yrmz&Q|W6YTu .E~a_c]b>~MS=K_X^m¥$:;XV9rKD>$T{=SndXD;2Ƅ}^>s4Sm1s ̕ot /$&6P$?\,CI^7.mǢd}8)F;ɍ\T=CI䄏ab7!9qd[DSV*揄hCVnb(s9^a^|;0i(x^P܋&`ٗBQՎ:DI5']zbL^TOR3wDyl},XND*ҲRp-řW@0|L>_> \,\LZPf.N_ݏ( p 0IHLcޅw5`߆jV &- ׃P jaS5i+/ EQsp]0rR35ؗŸ#pݝQ6A[sx{b;{=.oxYf!x^\_0gSySj ?5W|L(|. (﹟r!1Z})>T,ŗ~t'k_Y#oMzHQ㾚㯴l TUEiO׶[߿_{#ҷ|{7^~ 19.8X6$*BT\C5"@6%alK H 1WBtVF92KU|2T n>*ᐥL.;4# ~8%{T1 lW!h_i^LaɴѦKt.H/f극Y&>2MKφ YC]lvnڋkyס^op-Oʉ9O2ɥ_^Zc@TndJV"DELVεdIOpyZIFɞ1dPʹ/O\gω<[ñƘ]Ûk>`q2 $4ΦN>l1竘S%}3Nh-@\zT-Řvl{G`0L!uAQ oDPUAsnޡ-\>#c?{$-*Zw =^Vܴ<,Tx׬1$ A6m M,D{22(N5qeKѯӲM ̠e~iW lwؙk+nxE$s&FnUF*H1g)T>Kn=v+P iJrЩS \dr0PO+=(73]Ed\us h8ԎA>jUD#*^w=^w=?v8^:uhfzڶVӖK`styӗ 3#(lT`/[5iRᑨ۵vƌe=B%(|<퀢s G[: O߳ O}Vk"Ųoc5{%{%}(9EL~RvBjh^vK`o98  Fxڿ|?I37;KHR`}h ؽs3z;Fyzw_F7+eCvc]@Vv.ƌ%\ٕ1݁#xh3{GEAa-;!<Q}0/}tRw2 (.)?6  H7Ӷm^` [~ 1xIJZvnۈ`0nDQl}:!.Z\ƆDUo`X@E>/ #y{Prmņa[҄ޞn ԙsq᧯@q0JɈDعu#"0zF8oȡ}VbKuP7^*:;/ X5}3fä)3/=;oa!ʣbߞxgp!3fV˯EɈ24 uX_~aƬJ,R`o˘;f;غiAoo7sQT<s-’1dHb<<"㔄 ڍ f Z@74#͗LT݌cuc^dSډ&l< =1%XX62% cWQk>.Ad31&sFLƈBa٠}zh =e[ :Q+0d"řfwhAPqXվ}\fT #Q#x {ۆPiea|H>2CŃ,w 7-fR&^ms*)}jb_sd קxrC}tJ!GŁLm/BElf$ `g.ܘg(*yagg>LJ3#%M;,ȖZ:S&MUU{Hi 5B&[_6ѹHJeXT/?͇`0Q沪@aC^FXz}nŤeOӨ4eI9YtYuPOz0#'9X#(Zꎴlr.S+f"-3}AX+YOF֜A4Uk~CGHJ6-`PrpU}b{qM7C95E#s[h,#"#' ݃9Єy'TkS}LɔPCJL Пl*IDZZ- *6^'8Ф?b2 K:{B.XlkioV[[ڰ<0cc̸Izڰ{0a oӦ}6{Ctw.bÕ㏏i @בn&&|"`y'O2f]+ _1ɮ|r um不KO3ӦϞ<)ړl;{+*nŧ<؃ч_W|wpm?G ׿i{ SE^b}_-ىԴt! Ǐ?pSfZ4]-~S47ZV*gqǏ4~AT`ݚ]Ac{KϽ7zNc"Wbfv}bF)yE̍x CtTGG鹖< N^}-Um]xb8Re0R2qع\;7ËV}ے ^>Gu3.Do6(Fxcm?l4xz׻21*g8n:r !,@s0? 5{UiSg ЃA[wxl|̒gad3L2Ic{KE~l#[_Gs{uջ$cbYPHkr #堭yiYLM$ 0NHx L fW6j4l?(&8UQA;;L<4MbG6ayz8 |I*UzWhTEg `dNL( JTՁnF*:z4yeffV;z<(ƣhS 4hW Ì h IDAT:PFQBz&xҲ@O{?D`j,t/H&)CA jqn4lV!;Wu|EiчՖZTf뭢(>:4OUQm]c- ]Nb0L&YN''vAr@A#2osg-'2mvҙdI^ ry]QZ6}-^ ,>[0añd(*.Egg>Zk{&xybּE\=M ٝ_G_/]Yt`ǖu܋Ϗ.GƑC{ }}gi)uc#"zSrB/jhoӀ"ddXWc~w ҏY@ػd_0ddf1oUOoNjSSRRq`HMMC݉*z>g|'G'~ 5qXK0rKF"08}w`5Dÿ~d1̣x-+?{;OʋG %5դ=X?P'oonX>~pӭJ]6j FA}m %c݆_e|朁 V㽷_A#xGQu0y|~۷ln H>]t)f̮DA0ta|~59+Ė5/ښsX*Ho"U xt\7Oaa3[(X}m'rQݢ}vn,Wii X|/ۆ)?P}D˽3KcB(hi]hi-N;H0qp@D /-3W 3%}| Nt77O~٩Fbh 'Џ_;z3iY2ř8[P݂_}ߨ #?߇NMϣ'SYyy|hB}w4Dqf>Dc04Xe`/Rv@PՄ_B0<sK'b|arR31 DW3v7Áo0GJd*50BTgMW+`_2LYnAhe 9YuG|l:m:m q@&^MDQ`934g8WY/ájڏc^(K[ {P:(SOQ#Qx(Z;Q>c8> 4UuRj2wX1Dh%\-Dvt1*LEKlᰲ[7WAVV=fA\hnέDuZ%9ags,p7%_|wS+.AnAg^tWq-ڪog/D}:9c|QRZfv?½܅g|׽' 0saջӗ][0iŌ[?<W]#Me{{ _ ̚WR.@O؜ {ݛ?~"|;wa9|Mi3x#G'y߾ݯ~lh-{[Ey@$ş**ɴ}֍~}ު"KF(eF>Qcr*&/`^le/>㓨8șOWH"#A4a q̤31.̔s; f>]0rc8+CpX+˫ڽ⊩Y~]xh 8^no5۴Q9Ѓt;<&W݂>~p1t^޿ӭv>QgƼI(H8I.) >o']uCɜW3 I2PBa#f8!,V9 QcĘm-T3>bK׎JaAX`B2>qJhfM*WP4&9En-(g"/3 c"~ 8h|F{`"=M-DB@(FT% Gӡmu>[vxLZ8 g]5S?ݚrP\N3EAitW_`_1ϵi)/aeh¾ =uJ1>WG?%L$;WL[,ӘofSG$AiE!οnjH|DK{q|CP1{$h>P>΢\@:[=m[sP0z a(&5R9"dn&3gYފ67l>ZcY/r-C1(%|%N62Ⱥ>GP}\.vׯz<^\[ :C?dIo`%r9عU ocԘ PS0wxgڌaŌ}= #-m-97-8i.~I-!%ȳڶ>؊Ixї ׇW8|`vl݀~\o"%%F^nEgbTq\wz%7?"0yy|OS/s`} ܅s'El0?F# Y$0 x=whj<؃k =#R}_3G0`n"<{%&~xg䳭wdȤic5S}8?7O~kh46.+<~m_̔tK>EQb٨n®X]MXz[^IQnZuUʇ0ڽj2J-pUgrҰF x^LKF<5#!<}|ou$0Tw .WO_nOmAWDUڧ٩\⎳>OicN*P;x|&y)"$I \NJ!nAB7|x7cb n?4*xE}P9f'u1c:여Zp63X0$yU;`[$Z>QȌ[>У.._[mF/ }F"zCAC^<@MR`c4"狕+0`pxk-<Ӈht)^t C :p٭KdU-t*^{`#^w-θd *"37@Mڱw}oCzv*r2#軔> Gw#DjFM 3[.DΣL+gH>=Me7 +ϰ$v4\c槱)d iOX+f QhMb5s%O|'T܉zc7Lec_αa>&,"b) {NJ<ԟdhּx b!’lrd'³O=`P;~,nx<+Žފ];`gZ?>Uu1KB{RDB ⶓDު|wi0? 笷sdLEQ֯FwW'oY%gg֠D5F*wWnBP<>Y?HK_|{w۠\P猩ǷN7 |vʹ$`X[Ibʹ  0VaΈI{tF4NQӱ6am93Cv`ձ53..dL:U r mʷubٸJ\3<ȥ0eS>PՂѹ|gdK/7~3 )>ArIJ# :]X \$kZL_NLybN' )|@E?aJeO$F^~`\l0Bv5adS ^@29kV"o̗Cq8MQ0|\$ c$h 'i ];yřO V*@> c'kt` a cn^ܷ ̳T ӟ4_*#2S`jtʺ5,K*SohIIs?CJ\|@0Oy_Iv8?V 1#pT#E.Sli1AlbX딮 Ւ@H? Kgy7CѨ\]\8zӗ3`_U h5a(}LLZ0 |}> "<7x1|ѨM7T"3'f/λ~24PmD0ӌssw,Ť٪ 3B\ݳ]^Gh4x>N@zV*XcAoSYb=Ey~ l̳ m=ok{M,XcW,]II3K|+Q`oQi02WQS܇o9g<79Dŭ@zRPlo7ޮ][f+xg{2Rk%Xf%/S ѿ1k̳AS L*qluLqT'_N|fc/@a=YR : +Id-*C6V(/sehmKWU]ql!؅z`EsR~f8|.Hox>']^HW;Qpǭ_bz}Chc;<Ύ6_U ⳖcZw5oc}m{)C$P\dY!;DAy餆n_( "*Cfg+m]>SvDگ# IyWtZAM>~vȝP|r]: 4w`xeZ`RQ9f !'}@Ed/58o;F&C7󚤯d$ޟ+0:j:]풢d.ld;Nڞ"0FΌ}nn Śna;@f-b\DV4@o[? 3(䆧ےMY) ގIQLY<SC;/=׼ -+h4./t@P2Ҥf ca{xK!6=0;?>wJT!wXY.λ~hG#2/0BO&kŞLY4Gw֣x;{PP K+9qXN"sk_44aP^O[?zt3T=ԌCEo5_o~k?l\9ˌ}2R{ Eg1qyX4vwM?Kh~NN2iS,ddfr2T.Z^{#r6ᇷ\G_,*d`L$eDT@~:|-`~١L7M L~Id_4̓bVB7]0bؼNMYk5.=({9 e^YޞnBگx8]RûH> %+#6oPu'Qw/?$<Θ?w=.SaψWfy%.ʢ^ϲ7~K0qYVo 7Ol?LkKT03 x|@Vod'OmO =Ͼ ʦÛ8B0f76X:vt6u&f2'JȾ!q#4q S F$M='NOCN昂^@"[5e?7Mv<ğz;0 ^ ?!ZYGv{Zpꔌ_h˗CttYV/ը ]0"ۢ"{X&{Ы;\Lho嗒Er>80fqFf(18 kPGy mb h㣒 d%HN\_%WL`I+WƇiO+\^}d8-'xMT G5ßB4Uw7U4޴789彃XnjZ7-WHPL$`_pme;gv@NuOcO-w?نy_yd p:Eل"!skK, ɐ2Z?BH255(1vp4E+C`ǎί7Kcdv5 qsՙ4IN?N`ZG}z:{'s;"0spٕ__g5?pb4tB2;%j]>vކ=o{|(Ci_5"+U'Oa)@ j{qSCF˕,pJ>hy8{k&/!Mg0l~B$0k(blwk$ԻY*&+aFB8R-DZ PU՝lʣ[qQUb礏H$s3{dÐ,ehh J98 1 $'?dj[ju&lUu%M$[ IDATSdv VrC@X>ە|Ѻ ߃Kpp RW[14G/EE *~4mG4U+:YVţ܋c<2'[ڏG1>&ZԋJ/S4l<^V[z` SADlb׃) FcؿVLa')@AdYGɷCX0GN7/KԞ3>5Ŕ3W9'-BJXP#uZ OĉB@Og?CJ+8A{˻S[@ل"\ya@R?WbƝ3|g2%@xFس#i\Y@NY Ri#0)ԝ}&@A1]b*,'%&)XO1!iI 104I<\hNK_kݏ%qUGsavsݪɎH$lbvF=9-2dԷM2hBQ8-MF"ʍCӌL""~o<h?#[]a9vb6|'P2;#sXJdqh2%3(KFk0.cX gg}??-Tij3>!'Ow >4#cQk:})^?fT`fRop[j#ێvjl75vf pcfZaTjĸ9#L-8LʛmUb4^cg`ۇ5}s]* nsoj4_Wklΰ7ġ j-n$L^}]׬I POKOAo}]dVk'OgA~TXnli9v\zbm_T!( Kihw4 c0i^h4 ;?8H([N`ƙcM$Pɕߋ߉퍕Wݺ>#dc *LULJ|͠g~kP(g&3Nd(-E/]7,SgTŸ-mߺq5"aBm^R5w 9!B$y<ƯH4񺡓rԞT;:.Hoʁ P8gc}MDw\޻(J$ՋdK,98.رc?s/{[Vd5Z;)vRl܋r;ˢ{pr+Pu5rXv?"};(-G&Gw]+% ܋F F`e8Z hl'm(6/H--Ca]9;>q;]FJ38 6d6-lA gQj V I(d:]lPV[9OzOM/YV6 Abw}RVZ<06Ggt)?3w,a*߉A)Yx:@{z>)TxM'ıKqy㵲 LtN'p|D,:%@.;h`B.NMo"|^_\]<Ԭ%+N0q{v,c89kvΪ ŏ9%LD]KJQX'9殬@^qޑgk Z%eg<\"T-wXQ 4{FP}%!GȘEKcD86޽*]Ď3rʼn]ch륡8_(J\iMN43sr+m״(>R̘'oEc;?~~?IvrLgNBkSZ{<2 AXQuA }C=U >l AM=^5[6dH\l zl18 O±CKE!Rזgm-4 h 4B`%ip\؇#y+pF SpMch 6{,XL(Ą4ȕϜ3}o/6W1һ8}=?B(4ڔY#fTI2 XqKpm@Ć:L"gʸ<_ 5f`&o(?tν,TmjіEX\9clQ upL^>JJLy\ԞXH xhJXU}N[}%dUREg*U\]X&Uk=7x9*^*RVq1֧ک cܛ˓]p9Ż\a0d0`c|tB7{ h80`ot ^EB3=#b/N :}xG0>8|Jn!-#-zۆ͓cx{06w}gIIw `,<]ÊzF쿿H 8=_~~;^y,"cY+؇;(6ܽUJ\8c-ԞlhXY4ˀumuߟzx0%|3F'/\+׉@ڄ/| krLɦKe3P\R.;ݝ(>AIB']C7;n V9I~MCsyoڊGcg/_>/!=]ō7݉,ŖpErǛH{v`*Cj\#Mdl.Xp?=4ZFipX3&כXyE/2}Fo;j0we% r1t5,`}$d㓽ҁe7GO":ÅWER-E5.ı.b4<^aβrdcgMưh,yV+x]h+8Zkz< DF"hdF>ꫳ5z/<.d+駱b\䢯3so[+gQ+/bg@KM7?ёM_|~W&M&i;SMt 8 z 6,o0ߋ=۟¾`$<q8z:q1<_Wo]~[ߋC>LmvЎ6﬇2zZ݁ yD"hoiG~E%1K/)@aq)^"FSpyE\% ~烊P'swj龇0c\Ws|G_ڷOBiY%Yd8hW#2>vj6sނC_ƯmtspK On^,]&501С'r5,8>]B{l:k|r7?8p#x'Ґ_P\GP?_W?6 - |u|;O7==+^] |ccՏ_XƟ|3%Cwhqk SػY6SKtǺ^26N7o71gUo2JfqLOY|;-O܃s"N^~V__?mz>?.~ky%'?U\: 58{>xC[v.+1yI+,,W=~>|F{i$XIY~O~ڋ_Vv3]_{ ֌|w BȈ@?)%&u5ϊ6Y Xt@/92tzW~lO9M v\Bp9'=P:Xx=uTG0ʊ@j>ؓcG;B2Z8p웛0=T6-]AX8k6o C! ̅xD=dg`lBkۼ5x;m X+EU| 2f wo1#uY8k*lkn ҴVn-+ M &GZL0;5$斱LuwjvfRvqpAwf.avaV w2<nťh9(]śg#;/GFvs5<VB2,qecdbA]7 y\}S>6߳+ť#-mhxܥYRۑ Ϲ|ށh:ӁQĢ1` ,X_ rtl|RԽڊ^ CnQf/-ǂuUѩJJPdXu.iFgCQ>ȍ8✥/ףqdrn1Vn'׽Xlwt@{v^~&1xrly )]0d؍Y>oxf@\{9yX޺ko^ЃqfjA)VoY"k6>efMzl׍;x=l0h1=w1p.|1z;Jlz7?U~A6]|RT#ԠglFԈGfVYb;~bOS(ƍ7N_%=o{?6mc˿GO&ŸᦻП~ ^Z6핓/r,##CQ1c[ݸy `殺 vIrUTᶻ*|KOrr),.!#~4H{߁p?n b bu\JCq༌ʙ]/G~#wa8<7ර[xʵ7?:Ο9:997w 6o l:X.#ѩr&~K ^xQtu Ur]uolR0+~x"߃0cf yԡ*f56sHYD+ݟ<_£C: Cny(- +Ww݃(+Th @n~>7?'/=G,s%ez˝x{? x0BjYز ,'Zt5xBI^da^Q|elni Gnx'wcGa\pd-9!)uUKp˼ ("nSYn>{κYC3pǂM6wqK2Z/<_}G[ϡ<3=*oRr|2\/N/#k[ҳ[\܏{_(/ʅn E̴ ܿVyVl:8Y007U qMXY1_+K*Rp,/U6f`f.&?UiǞe5L{z˴#ө}sS"<ʌ74ӫB1*?[gukrA3)y;I'u殪DV^Ti7.xzpLYy IDATX}6:mv%*Z5VP [, T . rK"#|^(=ⷣDV-ª`M P)T&U/PhXKٹpl{㚛*"'K3ҰVx z*YFoAI߶,KzxF`9 3ӰvN_Ƶ:ׯ39ituu딓!F0u`\QtFGي1җN g{[X1EX1vu###%e/P5ICOw{PȄBix6s5>"chmi@,Ì9@JǒdmM7Ewi'eTd'Dzo@%%e6J%qŨPHc$eQ6v#==ee(,o׃6rL E'ZϲGhBAA1SjdCwWb( m3Yp09=g&XÛ1XC#JDaV{NU}_ ',.-J 8'2\4460r3P4'(Oa22200AAf. ȓ˜EOf©ؘ}l ^@S3y,5h[uE3!("j@[Wd̛laFq>VZZ i@oxB9-k}Mͽ82?O _%wL=~4 ( oϘD}g9*3$^.7O?fjUKJ `=vG^,qT HVHC3|)x:f])&z&hff- :F IVGJnxAq&Ҟ&e(z1q?&mhxj`Ú+9|lA*cN>9_RIP=O)cOK8"OQ'~L_Jxޅ?|eF'а$@ ?)I"N= z4\r1KF0TY_L z#(o,TTki|FQdǼFF3bkT/1 TLL╂mu/+?'DŽaRmoNR҅ PJ?>d%oKS?'Ѓ|kQTT~ \ 9ɝ嵼ڻ6qRu{9Ť OYsV-6$,Z& |3$?34!4! h[VOuPn[G5Tnf%BLE vWt+/k;;1@$< r0QA *ԋwa2Jl'$9.g@$tŶF9?q'u 5 `  Ϸ.9 }b&zAPekɷmy$HiwhT  (e["{^+>#пy@n|'q#m3/ 3":FFO33-Z+ɱ!fR^.V>MPy,|O]rt%<"쵴 &I'CQ^xs q2aYmyx.Irp dU{6JϏ%~{pIꌛZ|$O[wl6ٛIGЙPJ(A{<= OG"ՁB)_$'Iѹ\~2@W$ai,3̋?*I_ijIJ"5q'TwUJxL(d:S<EC MGO oٝ/cmtB|,M{1ͳäjj# 钖Uod!WNC_mFC >q̓ʷ'Ia@ACӳлF䋭 IK8W6x:~T9F_p<4dL@ rq˼EUWPN)(bL%aZbkP@Ě~B1$2Ԡܧ%ҡ RGBxq!4a_k@(Ƿe @Oj6 xAMU.dԇ쮁qJGLZST"Jsк7Ѷ[U9ѷV$l ‚%X1 Mʤ FB-hIn1Ux:D?"CN?v$~7MIvr\ NG| )uk1B`H2tM4/8j fje3;3mZt'$M xtA{4=]e,Kt)0P򊍻5fTDԗF \`gHLcNnLSJLWӒrR99H"[µthٴ+u^VmK\t8/#^ȪZDHP;ih;F@\iQ}#FL|vdk1ItQ,Ƣ،%]qRJjIQ#AUxs/ܲ l,DZ&\'ψS#$)5Lf}(j8BSxXim G1\{Iy~]g.mr4@!sdPd-15Iz5;CTJ4t)l˛^ˬidȧ6Ҍs\IIuV .*eHa4d}vVg2KŴ# i` ӝ,+ar-}DJRDG ;SX|F /.dNBV}}To^G۩ \?!E{. CZdW / K;D#$2Sid`l ӈ/cbtU2MAi;i:[%w>;%2@ݘStW&șhs^&3i<ٹ[yNɋq~=~ iÝ7JhwJs2UƏC! d* bQ]E? /QnקtB@M5%:9&l` jK q'&TLXȒl_Lzå.8n*Qt0 G0$qQ#XE,N s^3MY.L]{`0ڃ آ< eCj9M2W -ᇧScхY[% &ߧHוVBjaz+AJ {;>o u1Kە #L=[LXkyR/ahU_ePi@"K:Z/AE2azu嫡t ()aׯvB\tyɃcï =D hz l3+ ŭ Ko@ a&6}-`x|8u2%6&4^Yx)oNtUgBU SE4]czm1sK869ʃIEJ2Mч1yp;S<%u!EJuIv,Qk7d G~IMh q/ fAS/c8ZL{m5QfPYI(UKI#@":X$<el5ɓN dl]`ӂIо,Ia7+HMB>SN׾JS+ 4EIm@[(`h(.<8GF"LA0w()72~Кmy9zq/v=!Ht-rL:xFG~݋ nȴ|>f, қjv\'?I31ʚ) BX7@>LPTRZj#i *J6ȩ."(B" gBۃ`qsAWeeҔ1#) x [пvtHP}N8:s(7Ф( ;Ì2@15$u‹oeRjƚ-5Y@S~zh$:8Zn$ONcBg[|q0 0o# T Y%^̝dON%bS .55zѠL6rI6IӝBic2q}fbfAOSGJD a\қgfM9LPq;>t'/ў9]ֲuwi`9A  KPURq[Yc_]sv%Y""˅Z#B;pDu;VvBLLN$}"w2׮4Tj72GFL|0h$*  m2I LN&>+H5aź:p`U|ܗnBˠ_F@4 l}ti4ReRYI_S k7Y˘ػh"H+8Z0$ŤN i* 8K<% 0vǠo# WIB(Mm1|A$TƫW[ zΨӗX'Xv8]b OcpIx PPXX80u|% sxsL t; "O k/wNDx 0eHHy R)`6tu.>% "Y?ʋXSs&ՉSEӦ3}f4Q3p`Q2@c9{B^F%`F(LڇQZїyjjH1xSF8D^`]x` {{%$t'JYoIYJO5J'tu|fN ^/j IDATCOG݉ɋ`)_KroqDS~Xۉs'9 ʱb\#I|85.oD7 e9o ;4]l^ .-?(HPf&sܿ>~N٤GSdKq:&;x12:/F:Tʸ;zk0,t%[%d*ij(L: d_D/fhV4X_ѽu'G?=N>^ ɏd+u8.^zϾNz(&6œX)- 3/.ϲZ\XPP͉7Qx,AϾ8EC~b.`d>EmP2sqV|t$S;1)>g@PߚC=q4:' lDm[&0 mCn@ZFIE)JfN`R3 #ә+R¹E NGB]q|7&In Tl4 XWJS|f =QkT rRҌTSc{5!854jBTx rENo]$4^nZr}h\E51|L>/Tu a֑p }7SqjM} f &2,K_G'KLl z1Ek`rYVwAEmA&̸a^p>r_nHʰ6MˢfJB"lMlL]'~^sGt1;)h}7B`Ssn& P‡q0_'Ymn 6ĩkPY4 sIХYd~d8kr3Q6sVV"=#$JHݒƇQ-@v~&*aΪ ;M:) b7:j{Ezz9E٘U K_mSOh͜nhf//?[v\iTk-EfnD~ !n*e -ggDP}]MGN~&*`YL9)|D^ H58Kd8=b F\f4EN4]PB!` ]VP&>}T)F^Q/ڛs>]#器)MvHVċGy\n!Q7_d$ZdM u Cʋ& poP-u8D.+\%ﰳ#L"U7 G~1/3(`F ͇9H>(ҍ äƝ\HE,9q6ӌEd2L\6d8ǡg5:^:e(?Uo#U};G?xIҩ}5h^E!|V40׾ȕZת'"7'#/YWH+ab$}/' `(>t P\I?"2:]aa13pD#Q4;/7> Wi Y:}^<62A/Ȝ[#^8 WPM,sՏ9˔&'T98YLGQ}+ook}P"0p|%)L{7`=KwS޳.QP$1]IH\X{"-n/b"'[l$ҳK㘥zlA1x d 옘K\bL&d[1P :X1dAO<"!deflXKʔ/">r=tnԜ*NI/bps/rgh0T|?EkEuK1;/b}65-U0N^yJDt$%P}7Q$sm vۍua Tx ;/b79..*NJ+QhsAgMJQ!7|Z\GL~~eބyoP? l?AS; KV8h]㾯|,- `ɟFݎKwn_N.obJqz X&!Edw˸XKG`% +;P\"-wlT.*F{u/jbކFL[ґuH‘f@Fff(GNA&Gzq >msEZux,kp^Yk9߅p;~vcqy>&cQ8cn^aE+@GG߳L$JgbNAo Z03t6woTk orrιo:^f!|_bw$V IiY5w>q`$8 {0<`!|$ɸΐI.la@4JsBv`+x p]7F5|)guc9( 'Fz0ҧwA%iVuw*y 7 ԫwy=M瓳 7 /-ܑ^ 5 1qM}&QALwӘ~ ֪ăA;Fɒ!v߈ޅXqBXֵ(ZRhא?F) @t=ӸhaC镖NRÝsj|@l'Ue0'A GɑvLA@G%?p`\HkB սpr;r7!iD{OB)n`p 7wr#8:Omwes Q>H7!Xf79ky9n{:df]m΢l^1*y9 v ըH$`zZpn-s*7s ,Æ@IB=䏂˶Cݩv}ŕ\"v2b[㏪`4̹+?߂܂,fa<}lGObRnn8nrsWmC^ü+ǿ}q\X9K?ˏp3n¬I.2=nl}Ģ1<=FQYMs}I%KM`1/.GQThK EK "X$(YSn"7YJ"oE1ҽ'aMWuQd3%A@-)|Xh$\U?^\Ƹ1O4>s4_V?݈đI\\}~ 8 oL/*?fj("W/uCI(-,<, R'~$: lPgL 4Ie<О܇Mt~bo5cN =`ҥ{fql916ޣ4z7*b;o$MZ\JX=|ha6+66z9:u!E:X+"qzDэE=_ռ'io3UdS`Krqz={ _c@Vnn{z8@nqB'n\.;s4"l`/\zt4}C :Q߇ďrMϴc'ne2sKuKm=haIzOصoYҼl~2^cIY?}&޾9xoAZ|1W+ Jr:ҹÍx%q.݄-˟,lTy" &)ŹH2G2 KB&Mtu*$*1.|@#x*m%0,d2|'D['X/DTƀ I6Cc}&t&)cVEZǶuhK3ɋEܗ;D0& "fYeSeTGlsg޿ܥ=k A9H>> Hq{ѻ e]zDz _mCtm.U{dz]B5{zd%v3λ=2]A:aO! a{OPfmDphY6xGcCdh#ׇ@S?bјT(Ƈ?&]gj$}bb2~$ZYɪ 2OۂM3C#vQ,o:ÿ`Oկ4oij8ަRS31wMq,]pB=_mJü3H߸;d 62+Eٲ%?M2iӊ- ooM;ipYx/㥟5ps/mǯ51;qKGgaJ9_sHi!,LLwc`?LܼҏK /AD\S&k?5o0e]XBɍHkpAFԓgƵ0!(*Rkģ@ L>Ps9"pls'J_' kNuJY */" dWOq|uljoiF=ާj묓W{Vm쬺 `㴴9) J31nzpM;Y-w~r2Pz& vNu"@d򟦇3P f+Pm'F]Y`5Qj&JAGC5]m@BvT譙sб+A #m>(U<5Pr.[;H< Khtm[G$^Nt.|ޥnCޜb-$Mh]Q^Ӆ)Q JPޏ?*tMsCx&l'1أBpfku <6`;de㦯=⯏eo `R'.=v;AɗP~Ff~&AǑ&\zB 9eXMpB;U<ق΢L 0c\, /c h_KOP 0{B,yzi`g˧Fڙh;܈>VI_5۬o11Ѷߝџ.z,K;* 5c'|[Âۗ_߃],koEvӧVⅥK I#qsDe2{U{Ul!x99<2P=q] ΙL,ă;SXB~[dePPaiCłbM5khŠ2RK݉*# *Wp(:v] z3 9`xy s_t5D]fٖ7{qyb׮~/EIUzZZeg6\m*)䏏Nwڃ T-ڋ}$۵ܡ|NN9cIdJ?VN?3ʭ,+J`n-[GOT}ʶ^62 vjeYtR>_*It9t=OσL>۴4j l%RHSݡ8,ATa+KG0]RfBzbtB`d# pB( wcJ}`a⵳JOSu%wOnC2Ek /]G0T[=4n'Sw;Rm>6j^8Dz%zD{h kg!θYt Gd,elHmnVv Go,e|X߶=c dl>J%5J]k*RF~60N|kchx<.)kJme412كo;NjwŢ1Ǿ=tߎMv%@]~Tlzqp_w(L`8JY$9۰/i4 oDlB)[V%dÉcxG<'-? NZH iSevy9L q>᷉C$¯s Y/Q<߉w(5U^xTHA>YUJdyFTz <͈ u쬏<XזʃDͧZ"QEɀ[s~VjF 0%|^t 0{۽wd IDATspDe3}Ws2!Ģ15.I9WPQuSkM܏JGɄ剾GWK?2񎿼ٹ;L7T|rZDݰ‘ߌ/Pi9~!W+!IZilqb_ cpIj#~nH||%Sdjf4>ݙiCB/.QƝ_'Jȯ%oџzdQ"xiyYg4E՛W ɿHbʟo0`i@`Y 'd8ښo%?{%qމnLwOO39`(De[ڒ,{mk}Zw|}=N#Y:=?K֑eJH " zfqSZ4D~W_׭Ҏ,@E״C6 *<7ZlAvN6c@6Tg8㍼x k؋T%}U=$ku[QU_I35~Oc`[Gq ]<*^]`/5U #., ڐtetFFx<ٿCf[o#Rn #8 \,2hƽfTg1_8w<xݏ6b7<I݁cY̓BvH-eG KLG BhjP7-d(eBY3R6CRy!) cӍQҷcmU&!9Zx1cBgQ3&KcXy Bih: rĶ%Cpmc`q/~mɑV J˻83Y,TdP-8)B;'1yTX,`\u^Ӿ6/Mchۣ]\*`=X<43Kx&^\*`W×OyhߋgW?5T=BQīBN+/i/\ 1~fDBw x*Uh<-zWf0vj-ҎXx~h\Olf0 ܻ^;~bQ"+XO.~^'L"e=><: |>ݷA ):0j|nan Sy2`|K(w(쨋8ѼCr~FgBMw#wXswf^~e϶$s4"۔T8YPЉ~}DqҁYKm_7r(,.a0.Rn>4miU}lՉX ב~4'Yw? -9LǼgE[/^&ngczwVYQZP!׮/?1{d3o.ױ[Q]/HW/=~[ŽWh݅L}M|;?OV݀=񙁳wtԢ(g>F-xbe,y:zW V A<~G?^\IAYB'wN<ٙTקѱ /K">]_Fi<`;C<Ї1} ~2`7`XZ,xSˤ\./r?*[XXB|GVd s3[<3qVzd}.La@-2[\{(νЇoDb2qxx8K͛M\6+2,,,!e031 \XZ(``*K#8/>V2?ĎÒ}IVIXybE$qU G3(k+;Xeye*Ib )Mc#REoUP+z6RM&+8%ҳ,KiB4̉DCirrXZEGdGn"/eniN|quwCj[aD"fB򸖴=ƛ-8%y:I$O]ҰWxp]W}'&] q_o !^&NciFCE*o>>rb.}O4~j@v26ݻ.rb+#bnnڷ)na˽#'&usbi oȉ gSǸ+7,k;>rbxWrzm=5 "m0ȉIAy&&bI˾Tcÿ_)oo_DI@ :?2t]7]e sXCtc5*G>_ ߸/Xu\5nνL2w!ӔÞ_xMEOqW~)ܑS}wblu9q [ݟUBׇ0|߻SIS\*"q⎟83q6fn~<6`LjÑCT^)خ6{;6rg^y UnνW_Q.=5lX v3p{n̦-=8ii4nhV̪ގ\y/N̺nƮCﺸK!:WPQTjW V5+sܾ0kCmScٕl1 24 Kq_ %s3P,PTGqQH5%Jph8n "Ƹ]ptpfC+9,,raq.9 mӹNu@#uQ#9It sOr7(ikE|04hG 9F baR\Ө8<3Yls/^wrb돡*U†wP#%Bq/ ϲMc"k߲x0~n?Auxڇ75+2,_a3Z1l4egBEE{\<+S Q][QU]Ź%\6v=A*>\M02M;6Z+'na)_@1]oK^CEexZ by}=t|jXwOGTR8}G?xe 4كX=Ԃ,-\#?7xf~lol)E&W%?&qvՁ\]Sy{NkO!H l,8aa!v,|p=^}*QӐC="'~awik]駯 ?چ,߻6rR/y*,p|\A6al;#Xw _?P bvjmg^Q'uf +]墽pbԎɘi[O8ىT"s{)ipk\|"fPlb}etG"y).!,%,KrQ%zvm'|LWGKUwhl]H:׳tP#e^Bxy:/qeei\_-JԢz]( lj cMb3G1sǫAn}' x<;Ә85P2Q i5& y@cLqyGg0uTk-F^%+?:s ݔCZxG$Bo~tsxըڮȵxI f1zu [U'Q s#5]k3'PvoYu7S>AEPN̹:1kۮs4W`;'Hn*ϨWыm9DΙ[ʳE:0mauO>Ny>~ y{3MPNoeo|6`WVR)gQ_9*qٷť"f`u#h)>9MccIhb|U4|y EE?׭eTqC$&r#_Ja^ X(NW|\s5ux;~G:WZnjtBW`a>>㲹G_<'~!dkұ\WT՞lM7ѻ[:at^+xԐoGkoZz_OZr/=TÖ&/_noW\|K|n|sNDs$C~֯ʪ y:/ >;3NˊR6B6Ə}K &x_9{2n^AEe yg0?,c sk',wO: ?wb5VFŔXhkԝ $)p.:ʲ6"F:b|qvfM' uׇ"k=>bedHk=r8:; Fg(nsbdWԦJBsSd1՘Ijh DTD6* @$9~cXwW5?twSz(N޽µ<ޕ!x'_w35Tfd _ >`$moF5 pU X鸼nͶG6!#7L\tS|tp !TN/Sňd'~|\~a2MMbׄQ]FEU LޙCXK^W],EԶֈ V2]6-͊;5շTS%F 4uסu5C6ܾ5(245i,r\mkwt(lLJ񹉛gx\]chC熸†=v .6?e*,X/7| xrJ'a#ċ} i:w|Lu _,h aZR "yG ƍ׷5#8AuU?adq8Qv ^Ы~wrIN吾}QDYm#֪UE3ˁ ~*m $z'yN^դEG.R,2}Xס_v舉\^D}dmoo'>R0:R4uD>Xn U5h^yag[R92?be](H孬`F"{iY 8d<ϽXdlCkOc(1ĊvpxX׈1ܹ9B"ǮHΆ_\\s*6S`)RRdL)nb YG'0s:y=8naƋJFeqʼL )I\d@H1[DŽ~ YT.=2i\w(DtNUqDY8 0t=CM/6!#4]dSL>1:?&~޴JqZ8NpNIsEܼ4ޭm[fZP]QZa|ObӾn♟? ~|c( y$O}ʜhfnRky8dHb<?[Ħ_!*yYc.)fiNTTn.IFl@fOCdv,MPԟ#&l$Cܸ}ԝ3[ӖF)9 y8Կ Dg7y+X#Lǎ77җ5_hN}M3Q*?()/E? u;)㳘 mE3"AA>UU!%1D~Tu DEWBV֨XRas2MH뎭XPIԯ|)4*NLM;?1S.ޜ^w^s}N4U7q{%" }r^2m+i{Q`t9geˆ {<->6TD?Q=?Sx pCǸB 3={v*938 GȂ`8_QBv_ ;Q܄}kQ(cdkӨiRvs&e,Q1AEe Å;jW 󋘼_y2-5Ω;#\^k# ̕oQuォ02;^(]:6zi7ON1,p;2Y< 8ţXp(ȹAލ:VVĮ~ev4 3)csGHy3frfr l^.έE4/o$+M/(ǝS_>!@-kjѱUyHgDa [w@\hGLg\)B'@a;Y>+܄!|ٕokc6?\?}/6߻n<#᣹G\%;֧=v]X*qGPvf|gqg`Btcé'/Gwˡ .:%w.|" 1gq.پ0Va6οԏs/ \>} ;ePQBDSx95{'>kй?c$B0^ mf#j8іbGrA*A)():vI3ՙk'dmeP!KekkNuݩ)pH[)ܓs?w.Mme2XVYz'"6o W&dQky,jO2It$Rf)gЖO%ԥ\"^&3z7P/_ |fnN( DϚ6 +b"^#x>Woa/oóO8ڟ㜹2YdZbkysySL}~QgOZ80ى<=so114ωۂ ,.䟽s#K/㹿y  >AKz/ޟU,0\8֏#}}LǸ"ٿ穿|>y%z~g\S_UӔžwn 0ؗ2k¶:1I LE|;|=מb!WO2- E|KQ Ev| :nFspt?}dzQT'/_xPې]T习Ą>̀GǤpeNʨʝQ w2<%-?PxYH:q"2ӝgĤ ¹jdMXiv2H!Fil}(|th|̶tgIxJ-k:)qK EyVy)cڬ Y1i $@㐉VҶ5"<C){Ev„^9pxJdo+NI]Cggctw%&ؖ8OH.tqOf.yY,\ Qr,$uY]W'/\ M5a@PĎO+ÔqnD4_Ƕ Ϸ~tN#ܝU]]v.\WVuh-]bop7_hX+uo/MJVdss*RHפ4wiѹ?{>sXc~\}s~(u6g* x<{swQs6j96-'jxʚ"Ԉ`I\#V#ۏc 7;ūqpa{ SS"<\xm°~;8), 88=[QӘ<z"൧֥;XZ(W^c>u;w/Dti>ӟ<!W~7xs ?~ٚ j*|y5YS}Q>[Sx^q*. Ře"x)A@/v}>* 7r6%G1C; yx#~gV'*gF^<ˏ5d+p{0s{JkF;(=á_~Xi Lѯe X?uZsG W/zU~,(I`jƋl];5LxًL#r~RzwW$_1|z/zNT{ΡzN|?>m{BJ ; '~淛 L`anS:7))ww_c120|i_7>ޅ n5|xNdk3(.1?WG0m3>AǺ&;rZD?Aىi :]Ĥ%L y83=/ĤO_I~/9nw)+Gxwb‰@E'\@IyB 6lxv*ShNi׈>;W:CR'5q]"b8,+M{Nc_玜#yM:w $!s)WoiП̃XSY(Um\]tłӷ8ȿl\uxr4NxDzpp0Ң.M2`!yM /UUlk-TO۩dXNn^ⳘV9aX_ c&ܭ2dͨm/k'vѭd|úf<;Ǚ8A3,~{Sjq`֤1 + ߎz~I=8?wEyct_=/Ya߿HӰko=)La ʅB oDג#> m_a\qc`rvh衈a^;Fy#;4Ø+`ou߁KG07QtnmɪJ{ǻ5"i6g.rak2t?޷}CPDmc5WFC]k?o߸lo=n's sy\{66Rf}}t7b~f Zԣ# +փL%<ݸ}uS#XZ*9ͭLWLWՉ/KyCl +Todd$9Wl^#JX8EiKщs{gpLlumc>d3>s.t(9Fn2)Y ԥMOIr2ڤE$QjrT.BNWzSZ)lyMd*rX[$ uʭDz nuZOS]jG6hyquvQ._7,,7͗+#בVoM~TdyYA&@;i0V(b={ sSX]n"QMmhޮ@%,L/ֽ{;<,F$mɡ r"Q]EI W`&baz"2 6ug'Zte6d(,#D{uxm\=uI8~6{X[[]eF/+q [SX2YtաesZwu ۘJP1|6oMba2tY4nnC=hBE=GMG-);4(<<ЃֱaML Lbq6/(omW=Z}W'M9=Nmi,-hz+h҅/1~'72GgO^`Db\:q$&nMgo1Z. 0b1_A4'DBd j*w\ G .\g1xu v񟿷l (՛@Wk]*D6B-pOϵņii\5S4y"T,IېԌJ IT-xLJA7d)Q lV1aMZHg>$A]Cb&LV9@4Iiu@(83 _Z~z[p܌e5r=K=1d 6QXN_(^ݝIm۟3A/_F-y|(e\Mn ٘貜]m8E cJxSJ;N0%'C[rj.dlɥRDWP*i*/{F)˭ϓ&fm*Ց)RR;n\p U~TU˯#S9 7rf\h6ne^:ix) =l|EamN=4JqQ5Kvr6r14~ǥ91E'r0; 2 \!nfMRXh(U~2قeƢ0krQF5yΤ4(-I#SP:g!Ipq@Srbȹ%c5G()gM#5{SVP[NgPD$m-0p)5\&]OK.OP^ W[.sn4f$\Bhly hdu0$}ܹNe%iXFWs.>l;"h0LUOu x``_RneV\ G;/fUPQ(-A.91pBf*%Wθ`kP+qDDR!9E:Dj%dVj%lՐ,#z{bd_i=XHPI0 <:&S8:]cÐ3o(&&dǦh^]C95Sp f3E\DVC8ͩW[e-IS5iقȕ)_•6xF贜e stc$G+%Oڎ#72m\H22Vevl` c;!x?ruG&< H`| AP4yCȨ8Fv\?82ˁҴ0 \:t[;YQ;𕔯K4@:ř Pq UJ 8|`\,V-W.iCGałr1gi)bGf) =<'P-rvoJ)Yd6>I/81$34DfYL? mA6 E MՀ;? /)G!?.W1Qői+6cި/X^f :M""M^֨ck0^c YRZU zX1PJ34%GNtYAQ.4P2$Fi_)VҟJo(Dsap/mӱ\C9uRn(͢GDymMmޝqpd YgMAr1$nly<7pzk .A4X1޵={YߝMBŘ+Aȋy] y€4.Bf\ift+4.mgRlO܇@[$ vl"**SDL|Zaꌋ;)iW,I e>63q:ݩhčYtHrP6TqD ƁX$',6KMd5\AJ_cոҦ]Xե n!lg&3#Y]:4P)o:Zq2$i!a Vܤ;2QV&cSyleȔ[N+SBsATN}Mͬ[X~JD0$[ufL[!ԜRҎ,%r15pG h珅+2z^ʦXnV W]6tPL`Ht=pz4N \\J6ubmΕ+EׄKNBb"Acz9"Aw)%Z,ʏF>׏ U'Gk_5pq2Iu'Nwb%rl:R 2qBIWUoDrװd;rp hy[[):юRyףo$UQ ZJ\Z=LhlId::taBȥV$ɼo qre av-Y\ 52, =NuTpDo5X ,s֎Ci8|@(1gޕ p5`~Zڜ ]CAIFr:fOF<9ˆɄ>AM},s,H]AqU36p%^-WqtdNc^ގLXRLIEi) l!I]B>X|ayŝ lx Y 8>b$rQ"qq/\*K#PشxST ]( ]i91-<ڙqv 'Im.MJ3JCm0RQP! L+b *CjG4\%bIԹ.C,l P>ŇS 豀1jR cU'A0H%:B}:EO*OmYc{G: kB-i CEdKR:*9]_xqtw92쇄$ yE:-+iD]($/0NO.V`1'@Dey`55/rb)Kuy*ǎL'L%4)(H`)rI2D醩F$HQL\YR;Nxե.'ԭr`EܓhEiLtCD fuDW$ec_O0yyG݅|DQ<$\u~ 4R$ʨ 7q&)ĬucV<_emM|HFQ~j2تiKdSu~hb]MӐpj.r,W,ʖ_')xI]t2Xpd_&ƲaxL~N&x.o9 8vaL8[ .UOSh RqPbȉn2*Z3NBrgY)$f(O}nB& $>bLHmxS{hI#KHq%įYT]v'r:Q?XvPR'/@341 &G+9C= km<Yq>LƳ*"$ɓukS%o^4؇P&egI [-Ob@* xzY)&K1 Y-G.Bn|],PglOH΢t2Z3:f{7/s<"(QBʹ[T\G\:*gKeU U#w#&]..@a޹Mn=`AyPXVw$1NY"DAOPvgLD "!S+RuWC>sB]MSaމi&SL"i~@`J(I&M>{bx}#ny`bjLKc/`ҟAc%32͍V[ @]fu&7ƥwћGTTYI-AH7CirԳ XgZ]YtF36_}pt< O XPJ[#’7W@X1CA.}SMXUSӖY/h:q-J5[3k2ˮG5Cs4Y=%Z4$_N(W~LP:֥0QH,KFguI5m1hx:Y.OS <*OuhLVa. w oD뭏-IbPi~q'&D*S)#(UT%~ 0d!Mu er"H-8 E~J鎚_!u:LgFKIj6~|:DK]v= ykh!,U퍏w8p\Qsa\)'Xȹ 23lIN2,aVҮLC2*ԺИJϬrǚ'1%*&Krt:!e㸌araR̋rÖe.` M8aqg"3y4~& -KORtBt,,I`%UA̗Ds3"MpQ=#ʩq<)! lȴL !9P(c7< G%ocg].I^$mE#65_n g­ULP&ĉ_jDzS2J2m1OQd9r#ɵ^ЅWKIh_d/x%]$ȖkY* ^Lq߅\-Y1{15z^T^1=s`f^1Wx0sNI~'WO9rQ9rx{e=ǁ2׶ W,Ȓ_d78e~*[lst0C> ^WzT&淦8ڂɯ re(,u'mj?QQN"s7%Ҷ5"*Xm:%I$oH.J]hD$ҙTuLd&Dv&dtXaz_bC,K.*vL`f9&'o2 :Cfʵ8ssHq+etD#],S(%Q:JTm'q~MB^%2\MN2dB9a/e)Zf%í}\/MqT7ג|F3O>5DJZӄ)H9:IK˝LUm؞I`2L5h"df nV L (;1IqnlT>s_5J>M FPܩqy&.9S9$q qY M|yy&eb"B:pA8#bK0?B/H2~3Bα/깏>GȘhJ|%$yJQڼ)}BQJofbqԵuc^N!7!aԛcK:s$zlyJ{Lr:pG Bi!|_D&r Gm pE M>f$k˗JbjI u5e>O4c= (K̗ l}-7N Y.:9-Vq3L/:(#]>zOhj ֥ѳwL_x 5nL=YӒ&q~bz.xSY䁚g.q'&pR"*~D=)-`jHv@f(睏d,*Ӊ0 Rћe.=՘?fp v1 @*T9jp:q8įA$GsäH_0ù^IH~4UU6L$QEJC}r-MY#Kvz&v)tfFd )Xns*ۮx1,rr2R4u`ՓҰL .#Q@[vNǥMR{HfU/T֧]1dy-# kY8 }5YoDMODX'&s~{Xmh/rXyK1f`?veA1-Iw_;FN`$f'nJH\X|νbG $.sp{eSó.!SFsODV O=p[{e.![Fso=6B6xEˀ3Cvj'0?y5dѾ wcSSdSgON N%5Q}k }t;2׃3C:i4ժ D9 Pui/70z{c@]Kkb#]O&Gfp_ (;PלS8ߊEןǮad`s eо \m[8&% =;pf S~ )[#S%( $⨌q;sG3*Knַ!vb q ׏S&qob|`JS;N3W尅n j7x7/QOʩX >QK 2rGoM╧.9\;1l 0*O"V$@UktYSۣm p C\L/9M$HN40ӵ3ZȰR7ȩMJjP~rځ]5>aRxR՟),Ս[b\G4ՙ 5g焃SHYw27~Jaݓm{2@1%mfiptʴW݀; \F-N!5 +/S1XnTWԑiU!\Lb-6rG"YJZBIWCaW70XBM+p3/a# pS n:Fڡ(Xz‚{)⭜l!weƙMUzՊ <&$#GvlwQF{s\[3qSs=mXٺ4fFq- _ߞ#/OnP8#~ Clvׁұ:ׇ_ƣ? L92'g?} sy@CG DC{^,Nbn׏ \b%{ |VRyns_ĕDl/+߿_{ UJl9=;ڐba~ X/>TU ߳u3UѮՓœۈ: sK? kk,N=u_ĵ3?z?GRNbL\ݛZ{QP1!EIT:9?8cA~ғ$ŠF'_){m?eB;m IDATHQ@a5@hT X! YxA3&Ig\Y_vHx2.*E#Iھ#;cD4[ GNеySЮrZ` bW | 8>y~ FS N(lc5K5J{}'Gξ ,yf(P]%v*zwO@Mc.}F|9x`!:|W r87\]QYUǬ}l|лFq]ZP/)98?߿Ïo{NwMD$jV[Xy$;q2Y/x7鴧gL7n$3I:Ď-[vlɶ)Q"W p v?VuꜪ= @Wuw~uTչvQ&~t.oc}X"X+c;Y?J,1KTvC)/=tR,`+þ͔a@^ I"} ƧeBً\45^'F^V2_*#:}I,K1v7/+Eɇ>+ Knc%$됣ܜx/wB \g dd _9Z5SH*7l̸W?"b]UʯQOl #^-8%ckQy'%kjY}oqF EV? / @Xx'uT%k/wo3]sE|ǮBMMG~,je뎏M'6ݶ:4SmXqtId3mb JRUb0gbce224m\0CM:d1>cbgEǚ5mr3pd6 u8(>E5 ɇ)w pʰO"Q-Nor*\OB]SqB"U)V NG=Bdbܜ:BIl|uH>shKS9>soP?iX?Ve2rMfG|: ffk*p8I}j+M?8I/^mH Z45i?6l 8/r5>ƹcF+̕<[\hMyPdrOb6#"+Wn.=Zo=W0ݲʐ'F!!x$, ^A̓:?k[ڌ?-]pí\f6+|/^R]? G^l!yx@~`+x"$fCcݷ_{[h2719Pi lvܲMBO"b-fRda5^ U arW) !#pJBNʬg([P~43ԿO>Z%P!cOG>2h~!:nj}23ү}7& 0-S/o c1K,(@>#8G`98\aF.) t8АP&4ox]Bڿiqo`.T,TbE>5[1P!V+IUU֍(fhcop>}g^+Ѫ^:5#3}b7[б*?-EqPp?l%Ex=ojF-\[{xgD| /7?&;aY.ɛ_65fJ8J1y(sVQΝ+O+O<',&uum;;CO925~W0>65xe.фUIK)pR71'a,kaOJ JJ7`XɪDZ1tkpUbup;yX N`!ȩjPE~yR6^uP;YC ;v$,/sL,e"ΈtnZ-R+iFq~C[7u7!Ǩ$_Wht]7qC@ʮ+K s-a𔅷$E}(4+f$ϟDE[V32R31xt[K8yᴇV6i~ "(7hX9Js%ZL07^ ؇ajƄY6.CpuXn)a +6y%%Mv[YIeRl4pR;bNTN[FAL2X0bGJJ0+CuH[%>Е%}Q3 Sfɐ/f9j S̨6 #u/\JNa76փ7prx{vC!9r뷭²%Yҕ8s". V6/i©> ÕIעcE߼7߽6ZZ_-y _*vHX @76醊hdXh>q] <"ډQƷ Xbf y2O/EYg -kCiӢS0\2Y]iI#++O$/4ٵ\c֪)@b*;/Xa~r@Ÿ,ˠk1qYIUPcVV+>kcr7Ƃ`Yk GgX,N*rs'039=]kmnh3L^\7g&r6&[JaWPwB۲0-+6אKx/x5xA-M I0NWgy/op4^}N;oUm=~J y'= 3xmذckۈ(DX{r4/i<{?z3+-eWN9+(#{(cM3ʻ?p-85>]+5b_OTR/#X#[MObnZ2I{G_y8~w/.ll7MObjW_Fq(Pw]q0uCte͍ sбM. Y悂NG'҅v鹀dily]ؓB3X~~19J^y.zY(ftz9\ ,ò[ʯ쓙cExR'w@/lN.鿎PXoJxs[ȅ>ꃫ_='9k{xo3W1*M:OYE5Kt4g,&"8"c"0t`A/-*TP\ߺ*\|Z'i%PO2dF&ExӘۤS&70IҏE%|kjs2|]M!wƧ([HXYlȡnɹ,> ?x1)ܑ!-kR 3 K%fKIڨ0;.R;p?<5@2ҜL)u~R/o95b% Obx` >\:w|Z\+>}7& ;VcM]hhܜȅ+xc.ɱi/ފNwPjkp]8 fگ>:5ki1c0v)>Y! #b.ol7mB/9,JˡfplYtv d'fOUSSo[FfJ FڮS8s"&Ʀ؟S]7Ѳ5dIL0܉w2[.9>񷭗$ ]rBYG9wܼ,_`%V;1o εd7g8"wy# \2!X-ꅌ[#6+_`.U{,n_&qrl)CP ҍ%x=&Nv붯}qjM g)m؀5[WHN }?$uA5?h4oIW '&>6X%Z556ٛc؅RIɿݍMcYs-x*ga0}q TT^BQ'L'GM|HH^)49.#<)-PZ=rP3u5"?}طTòE7#qqo_f1l"=2o='ACBŢ>3v .= #Izɉqр+!~"+ٌSqOD^9.Hf!vn Mhş > *x᪦ELbŋ2)21fqhIeڊ‚^݅ewmy|{vX\^njM Vg247`}=Xys~{t|ڲ6{xʣNÛ.e6?k/Gs{n=r&{;v$obt7wჿncjk,;P<7 , x3Ue1o_߱.85YG(tnGNGvʑ VK?:+P X}6OZQlܱڍ3)^xFlg:p S&Ff@wF,YJigiCpƘ3s̱ <TD Oh)K7eAFEHrȢ (#E>WE4n=Y~;?;ȷH%Gt2z|쳴a5K:5@d;+0K~K15KMre=GRa>ee/㴍~>%Х92Mcy, ?kIJgpƉyeG /E2wU ;G_71V/A;99)=߶QA)񑉬N]C-}թprz6}\Pefxӯw:Je7jV/('L*g@iMx D m'Ar,ȑ+V' ?|61LZJ^ߜ~r+V;U xiͶkLy4_wvuNxa"72CaGvcBw?E($ĕ~GglIke|B!kwH*Mo@4PSP@e2eaay}*~Obۭ[l|r9Z*}b!fRak$]9yI˃깥55J3%4E,.kT#?RV2fq@VKM-#& \[HnjkӚz7Y)SLݭuNN13Am} Js w'm8l`N΢%X1) VeנcyGi}_4#* my9p]Wvr=iݾ_҅/VPqtp/t~/>K&r)?>Sf"?тT)ƉWlȐY[)XƓݵ" 7Ur Nm$yFƸ9~\@E.%^!}odaIcj. tpebyGƬE+ ?Q4# "|ta2xyPݼY(T9 !4iʂNWc6]/}( IDAT4n1g|.^ `e}F9Z,1oE 4%07[y1PoVtpFpԕlӱsuzIsU?xrJ 0=>oU{IOƛC`vzR9VMG jfV357 %~K]})ܢ?73'7A/t^/<0_W祀[l-ozb瓷X) YNbk){^|NkJ4c$Y6/nӅµ)$,NYNsNXjV4 c~7Me7@̎R^ZU?\vp&Ϋ l==/9^D'#} ePuqTC.^;!aʺ!bdv1Oq%KPI"`>^ Kɐ~\"x h8> U WsO!z pA#q*-FbHoe]{%BZIx)!|Eq`+a1%gFqep,e־@~6z+YZI-)'.OҙQKXwP9zzk723u }/f=I=.@i3.:4rJ 3/ӗ3~KXɇY3#Z7 Ԑ=NNrCJ:S/?u6᣿tjg/gZ大;zc8sD.MUONgcdpM'qYΓ!dJI'>=Se[Li2cB/z!GWg5.-R=-~t[gYSct"V_Vڹږu 4~+[^-(th#qB4Q8nG,H O7.(-]KƊ2NX)wƊ0/B8S~[b?dr/a U?C)'El`2+ B%XG`AUYH.z+0KEř*:II?VwlX]oP$x,&MI%Ȥ;ڷQز KיU˳۾=uIf^O7ٺ :4-|Go4DNmێƖo=i^3^bx(g2&u&w"] &fLaq;yc|S؇ Œ?t㎫-._˾/mvHXACc,՝=(+v}5@kGm[݇.}syk]:"Td@'*8 ;Y3 -܂6&VQu|oi>Q&086r?7V8"^EǬ&/8y6]ffQ?HV b}%eCryE! EyaFo{CZ@A kr"2_~XJ aJuݮ"Wj󓮳m7 e񈰷TKSxs٠-fP~q6 S iCe:D2`L=wiN& \/f6EncVqƊ$Ò 5{1 P݌;l([V#%`ن|.A]/UV#c"2흓^Ϣ$5iv3-Fƕs"Ay"؜$kn]X_wwDދR89\]hh)901v5n }a-\dž12p{Nb+ހ ُoķ l[;g]n}x=+1  wBSG.gpp׍wdۛ~X8{'v` } @EkϞ੘Vb1=9چZ_SڰX Sq32o67܆&b)b|G7݋}Oĕi>po[)u=wᝧ1:8FS{ Ogq0ږ /*oNlFb}'^kfgо?w2ev_zT[W:a!߂1p?y >K1r l{vss?>}ϞN7ÿv{Stm%dz+ڀHryq]&"S1g:[Y0IUngԷ&ARH'[0P-7fmp5TVndᵃ"ҥ>mŰ{腹?{® QS,Ib"b(̅rKV3?8Ǖրi@4ʥxJT$ vb}D}1?Pm¢\Vq)$s#r2sml = -Utdˋl_JJ}ÂoC") IUy<|)XGQbz3]4_^܀LJ kHvIrs,R,N/r*PQ8Ye"}E j8\M٤kJEmzq'݃'.ce;p$\nZ\X> /C:3W/ w[7ԤYPV<ՃtzD[}9Y]kLbV44Nc(v}b6ۋ}dio[n|?Wcx` c,,_߁6,Ο7YcE+VܰO ξx~[o{t v=:tG ̞ݟ M,47n9W xSl:Wq49ob6Z|Lʳ;?)/їXz6փG?w}8^m%>Sm>Q"UݏeD"./ڮdYHolb*pL 98X'ޘ /Daum_Q 9o-P9k1f"0tb>Ei4@}v4O7?+28l_2YWݛ'̦xyE֗blUr:3/b6pтUG_8x2ؐBH:/T2oQ{,Fy`r<uȱ)\ \>.!]6U]O$;٢2˗ijШE| pqB6_ߏ~=\KڊP}eU x0[8;֞/l\ CcͪSGOw')m /f'_zo[8d3owAV51Nϡ[X+hT@vKn% xoéW97Y4ԡklkVo[Ƙ_߼'vԞs.mlyV¶mkJV&.]kvesÇ<NjbくGEcMb xs(o8TDξd)~]Yr/0bkPLE$(Wtҟ)>.\ѳVѾD L=8[m9bplN~s|gW^SXwJ ҊJ%( hYo.Ƴ.SoUO\k"fq?_&y6ٯpYj;SdF?Y,_ss#[qn.`-TiY34Ƣ#|JjjkdƐ!E5RSٹ!xLrX1$kOcI8Y*022}7_?z9ĕ[qKgJ})WsYE=9y&Rnz^ 7KO/VJlu "S5 qrr>_/.Een5&Nxv-'>>IG"R"?z`B,pA'@(trU ;XYyd(F9 ._~jsM Ee\"N L?,dB9:O@㫲UHtU.'VŒ{ƘPqpJuW }&u $Hj4bMKg^4Z1H&ڋa>68|ܣ4E5zvRLP$93phktq8c Mupk|]}VuAd!{bZ*0z8Կ$+XB>UVȴcqgᵰ抭g7UÓ4l:W0/{Ɲ<"!.Hy!ɭWh%񑂈 .-Cp-Siu\U.0 E O|)+Jt!(h:7N)]#+R R_0<,} FE`m*&}8^ yz#ljz ~v/'>NfH%;}RiB Y/"cDǰC!z1bEj W0hQRfsBfwwyDd6wqqw 0d .*ʻ8+ ՀqK;+&V(U"[$WGfFu= 08IJ@i8\c":hl_rv.++/1lCp( llX8JQi@>f )·P Ѿ,~Aq1SHL@L$RA*dR:yx;yL&mE6e'NqSʖ%sG8 -ANAڀ#E$A `q, LA<.| g E:hUBNX^%ƈ}gG!Uѩ'D0c¯ح$B:.l?/G>!_{E@|E mu߽b"`!q(j*|\,*k>3 >VgjT|gPTy@k }*x5ͫ_xf!iNV,ߣ=ħ1RƩn>\|įkJN(0uS%aDbnTX Ep0'YWe(Q|)TEse>.}^< Oҧ';K7>C_  \((?\ %H֕ N~t1S)})quqCm3)&潼Vy^G.L!aB ʐJ sҵݴ0vq]GzGza|H' -W':դG@<ʼ[p/聄QkM{O1Qh'8\b͸uv1ԚChvFɓ:)µj-0N>躺f&עZIL%9*YT跈J%KSF۔R@P'Umóvf\Ҳ%Pv|2:EQ988ۺNEj꿽sOU-CgU x0%XХ%͡\b[}2z I1CyRlP hH:TQ/~bRVDNj}Uw/V&f=w@ai Jt^N1!bIaz;CetNq7]zpZ03E.Tw}'4Í]Ajlh_'ZZ IDAT4DG.AC,F y1.zNhY㌗l2C}@|~oN¹࿈YVAȜ2`B堵1l[d< [ORy>YgţP*4Jl\?b8M&ve7ЕT׀v; ש΋!PngR ==_>FfT/]O מǜBEwV|owZLM@|^#Qis<2G%sDKZcL#7}F\m& $dC0q BxFYJx؄(^ͫC쐯J !.[>CU\7O!XSO"z|= ǥ1Rh7oƬSdHHXAyڪmyl3qIB]^A!Yu?OVXmc;낰@vIIf,쀕mJY4 aBEx);@"Pk4ML HyГ3+-Ɯ‰lYkRx)K1q3Z]ڼ9]Iټj#t9ʅul"z"?"2DWϪ}O ,uѾ_؇Hkzbd_ 07*,۶J^J..ǗO!`Q8T[!U;_~/ŗN 3@Gno02dN*Ď8>>mtedx8O;#S \$d|WҤjec+=sa.x7U77u"IW~3k5LŒ܈T10(Ȏ#Ihk9ÅH/_ýLAkUY/IHE֜#[Y[wŴGЗXvڣQOƉnrJq>5#H1'Y,=> haȬLbHHakavVbfvb`$=ozK-JdG?HHҳ:V_Ea*cFE~\|D6fۭ&."pb~pɡ.&-nw4.9!`Qp2kvnCprwya=Bo8Y`K i56\ۿ/G*P?Llr~1xPxT})kÝo,ׅrgܢ&8]&xZ8طUHBpOqܯ9.>f<BY[ZMgbyYx`L<#zIs1P;)ʉm c葸HDrGtaW)b5QE ?RLY,p=O-Cq)ofpt>cN](@ bk)C`D"|PX\72e.bpvr%YF/~x&ɓ72dل d\ڋ_ dS]E(w0;i.x!F+' G[ =E"z<'fgI=q[WZ.Y᲻&^F>q܅1}ܖ 2hZ\$lg2sn9e<\Vv63},!}Kbl] ̪oRnjzA/oOAq1$3.TY6N_pp)My윗<+ UP E.;m1cUZC"cpAZ®e%#ˍGzV0ۓel)Umi X)^1弸 `tc|eGL?f]ui6o#zEmȉL_z42&%WdcgK/E W*$? 40f}Q*(0u02U?$x(-p[dA?< XE ^;rpgxᐕWӁ@ я[r_ou* -RPaj' \n=fCR{4^IHK>σbj F KZbP$)-4n0Le [% ErvA12 )L`Xz14 +gSW*^]FE!"u}-Np;ɗM'^sZRu+'q64LiʌP OC2B!ݍpOHUrHbzBnʋh<0^N=DfAA EG#Oe?t,~6/|P2\GFirA%?(c?,rǬo|Lep=Y8(o.G ;SWf5x1*Vҿ>R(;͛m MPx#Wxl 's2+APT^;V*KTlMe*_Ee4pR(idu܌61t˶N9 ó{S3tQ]Ɯ>x NN>yYe!<n%a6[IDE.P_( >YD HZhR.myJMCuW.4m ͋_MIJ\&U1yR /`t-vG5b~q])oŵ4Ąq^bm7o zb:lTHFfDwٜ[(BZb㫑 k+U遘hF!Q6kD~6GpS\D0b,2 9& |d"FF'`)QIYd͌r#9ۛ? "!gLmeJ\|Rz}^]`AבQI2`\&s꼆'ޤ&KvgPNƜ^t;"AcW:2YaTLE/ {ee[WN|b ߜn99=c]s !LdC88m}\gF},͚:.3 !WPYоX&_ALCa_m}*L8Wfgq0u},&U_JUşMM eR%{J&t7T].!4%|O MaߥS-=îF΢|(xhVF54zR8b0:R|-f kϴfDo9 f2%ٽH  d<02r­eW!0cmbIODD0S$0ӄ"iY .>Cc9b->|R(M>d)פc<=j}3ZҵFln/ʼnU{ֳvpf! N++??M yo _*i8RLo0E> %GhP}{ lxι,뇃|DO Dm(g˸އ<%'.x+ lPN7\Whw> >ХAq׽˯~ mKs ħ|*$ڜmG? WvSu{yVΒgQ1g4,guK'V7wf280?f$:>;W/𖢧ui&?>ڦ;{f-l8La{Ok'z[e ;;v {/Uc)F6t3뗴'ǏֺH@?ާ쵹ٗWE-\N!ViPq K_tMMDDžA!T.HE";r ͹#!.>҆C/HkVƯS^`l[-uMDG1'iŇXWR ry6{\zSjkD}qa􏌏Q?N''Fq`s0p c#ChB-غ~%9Newm͔Di>mYm*Ҿ&N_;ۨ{y!Jμkg#!}m2"~`@' m_``cōD}\Nu_?w;|dȆطPG.H|#Զmoe% ]O{'g+vߜX̅V)-bUXZXxLkyIY8f,b(?{TDd.I7@ i\ j %NM\ ȸs&QC޷[hq%PwiSCGYIܓ# 6-ڡϵId ʻuO)m*7=ix)'!2y5'_[ 4&s]}7P[[y Hgˉ6|< mf 1;k|kQӷ3s $ƿ !C8`E8&ivf~˿/ƭ=H3,1lڼU(Gh L*;0E32N:ƏG% &'$b+c?R۶"DC CTWnV^H@zH !o^f6 H]/0sSPvq6\k+J66J< f aqѪ1m.sH ^u(]3G&&)HԹCUŗfggzQL\E4C% ;5ogg&e|1m4 Psи63/nߐ`1\k8夲Bڏ\g->v}Ʋ/0`inO&įb ikGH3FG xc&l;mĈ`R|苿˸?)V~{2#]A,hAo%QUn곫ӧd~ɮ+̋]"yrq+KɕTYvwkR<͡{VhZސo5φ2N{6cKl#xz s3yeF}mVsu+xq>=u߭_|Y=J6hD ,}c'< '8 ;JT?m h y8Яx~4Ct<4YE LR#O%/VLߪvtCկr:O>lJ{x;0^Svقxܺ!~b yuً'i7?grK%[_dkXjį&ҠֺFA[]5]c,#1p&we7#ܩNu&^}s~!5jk{YEEϮt -`禼FS9fǚDfNҶ9٨T'$$h;|<ZAhdv3W-B+YoENL3CKy;^$*ĵ: s/Qek&R*XӘO#n\B)UCZ,. qqgq)Bҋ \St~q-@, "Y4tXedH`88v} J){![[[w>w>+Xճ0HRl3Tn ׅZ Y!5YvѡLɑ.c2QD&dP w,`=+Hw跲x؛4(vg؉Sb]f.Ǐg [IS7|uueJGfsW1h8t je语|s>ն&l!ӛXڽXx$-xLas)ltiٮm 2Q}T:LxmuMܦa54'E9]jjT[ɹ.Fj[CV,=VQ4&jz[KY Ҙ#4z0˚=+Z/tVu#]v៉ c % IDAT%4.25FFՓeh;\zFCqN4G2io)2-ź#I̔fZ߂z(5Ky)LM-C9H'4+0[$Khm@S]#jN'Ǖ jR׈zG^3%x 'ލi=N$O }/[ BڅJFZJ21R߉HeC%_#9}='T6!6. Q~?z}O^!{Ӭ>bZIb/]Lb}05;S&Z,Nn)j|N~򈅠p*ӮRr`/df<` Ў?D&_hwނ%]&1 ^N:]KiLM%eVt巠~rpCSqpe@̖ضtV,c=?>;׆&GMq,]5mE '.iL47u9n\5m+mA}_"J9{?9|s#䄨NIb1?9qt?`{?cpVl9)v=88#Ãh[҉k6ߋjkqu8sXڽ8{7_Ɖ#{ }WS'^CWJ|𓿅;Do "(?8~G~w`rr?-1p8`UFu#x轟@]}C\1ڢK}m:صY;62~Ξ4:vmwnz} Osss2ix '?:%^ё!VL~ķƉEO:# [X:Wg%Qg[~FFtY7n h꾗O{_y .GKkm؄~h =w⥝ǰv 9/QoԌ_6Μ>"Y_?lMR O~;wcG^4m؈G(>O\2|SO26eݸ~!nK˔Rǿ =:~ x?O'sEFCK@VWZ")}\Nvmл̬.͘~\[_f M]In6!`n:*$[qcZݜy;y#[Zhoh745Oe@,gD힍̲©L/[mFbq{ttZƄv>9 Շ- &_V$ݥyS H~HAxT[h(btF0MM _nx/bjyK\TIablB0J䁭CLGٟb|䪠?~;t/y[ߕ*aC8x{8Sѳ89z;U.i?w0^<*;'OK,L)>6ܳo_ͼI/MQ?})'g㬨wnl.JbZ]`vwkb(t#,v{~{gNܻ|\v朧O3yǝY(DRn&{ބӯo*Wfo5KmWb񷟢cVO\} 7d)&D?̬^ҽtT|64M x䶳ڊǦc2fن_{p`8qן1룗qӑ:c: !_} >{T1|rb8b'@}]-r>fw!G!5-Җ3 !9c>z)A=Ae1drKOl!LEmlhw^;} w`Ð!0糷a2bhyƋ.N 6%mj~I(+.c^ߗ|'mZ ~7^|M}/Ϧ֬\W0_s&ؾe='a;֭2@ܾ Ӟ b Aϟ}׬[z>uҙW+HsdS@Nmٛ “8jⱩgNfۖ i7 ]%kbz?0wgx11~$n Ix@A^L}y5UcU¿κP(ԣALs];qUbW[lw k/?_. ~d>"ox?|JJ?oߗ7#Z[vƵW^,gۼ |W_z{k:<^/}Ӣe6طpج@QO \0c|zޣ^Bo%'`'PĤy $<^j{xMLݠ.fB(`Ie].xP܈Domv@ yrb |4Yq|2هo;2 Iv^ (Ķ*f݇lu.\$ ֛0}̐-T jxaw(Wd ejU!0VsF|J5|~?J쿵ھsȮʱ9nYcv>B(w8 5jA<y-+J};Gfd%t;XȢ5l!(.؃:˃\؀斵j.mJўBk+|*K{`ԑоCgTU`ͲEؾف} L(h()+]_s&r *_܄ӟǮ#NvV*}X[@~C1l8%$!?g-Rކo:ϼ#YmmY49Wf0wDEE0|nX%f07}kD?v{0tь삼l=`z' iOĸƀ#Aqa~^8ׯEsPO9QQҁo =ۍᣎfAޞ")9EioBbG}Ћ2B0pL޼ wP$&S,7o7xY;TdvrGY{kO=OKbvuU凹z娯BCѹkņ51q)yX!= EūuWbW00`pEMu%G-]{~xk7[rag<ށ3OB@v8~iޣ*n8Z>9OoE@LL,N9,>Hţƒo 9Ԯ=qı(,A*Nt*z썪J,{YM'!³?S31GU<ߵvE% >h2HF_ђ2a yl{m'=<mA`N0凂fPA7bq "a'zbPh91[!'|!6ܼT~ tE^RJoEcۗhlO͟zcZ ><_ KJD*U_oɤ]I4[+uG` W!lt<\7:$duP0Ro(=H XYNt@BT)]E3񡢱 5ŦQn/7S?ƿq IЩ vZcѹk?Awi@n$p][Sanܭ/oAh3\d0O9&ump{&܆%}מ ͍uܵ:w olޜY3@sS2+G|b {m!2:umENdo[zk #ʥ"cg36 \pսx򮋰y2᣷Ǖ<ـ‘Njk̡#Ɣ^GRr{s>ozI|o|ricfs#qo";]sg=w=`kpJU1x|LGo?w= `ȣh/ېQx굙 G<_xpMS1q904553{%ax>/v\bĨ#/}bTWWY/eQ8}A 8=xp 6B7axPYY]rIX"I/f\wfs '_GJ㖫Q[[ ?3yx9+%_O?z|veԻWx|>\ufso"9sbq_89m@^A=VKT!S#~tk'?( *>:F.ќCT ?4;*k(EYm(t-1M~RYQPD3J>&=B:-6p5T)  n[ q5U@m)xJL]"d߰TKh=+!`|"x=;@)[XJW\CUs r>qH x\l-꒍w#AtN kJpbl)߉ek۶Tdc@HJ`|PP[qh$Ωn-mǐ}i5MՊ28".ނA{"1J})•\yۋx+Q^ѱƧΪcqCvl\Nnn :mP3DA^6`S 8 nCC}->yQL~1ǝ~C_ξt @`E!'{ !()CfVOmP7 }#$D&&boG?^u/b ̉ ?3yَYOt=w?9 Hp`ƕX[|8\RMUB~N|z g>V~Kn+ypsg3A& &|Q'*xӝYBf6(Z4ܙ =7f؉C0KԈW/5G/xvYhllS$;]QSǞBf0#.zaO.,3eS(0 ~1-w߽gYUO/ AIq!v CG<\s SU7)ʼnٟa@z'%1x>f<쟄ϗ0 a1y{`ͪشa-U$J@HL9vWERzL*I[ʼn~=5]J+w[6/6Ima0 84FuoIW!_A|?=s[%Ɵt2X2e%V tQCcM >њ(9|iHJ p[F :d )UHnX3߱NA3}Yjlʗ):5wc8圫`KxƯ6L!.#"\ai{gX. L~H{cAj,vnuwiI!.NMbX],|]m}yc ij2%48(G3_۱L22Og~KޑܒB\~d\uj >tݏ]19z 8ukVb:ꐭ ^ذn5֭Y)tYcWkӭՏ &'kDA-oO`f\vu+.Z`,)i3, =hՇ0OMKQO:' 4=B͍)znB=Г0!5bZLOc4&[T ޷=2X^耜8~nO g[͏ژu%uN1@1yІ "avNu`ZCV&|;$J)!(+{&w|WbB^M/;݄j`O|dA0 $V*c1qy TbEŤ IDATu.~]Pk/%CDEC(Н2L ozijl4 2  1% * {jPu+XXx轈@4ۖ]F.)Xt!r$fEmfڹ}F/ef%o&aq,ɀ!`I;mP '6)8m؄vuETR\`?`t.0/Uoi3E?2?o~_j>~ɠZ'5 ù|!j:7p0\wBʕX: wo 0锳%Aֳο,Pשs';9R jkez2x^t6@Ѯ]`; l`.]1jj`_F`k3 aKMff$ $i61cG;l|^2n+)\>g8"?J jz٤L:QS9ǃETfnN&&:TQMo#Xق}H`24vP9̥o;e0u~ ,"_emu5&vX5hB:Ͱdmr \-(rߚZ!/ v!O$G%UF4v {ۋT&M[;s|J Q90` -6=п]wx\tfC%w\ݒu͌OVbTYAđ4fh9t}{>v;|FL#CGŖ"֊"oK¼_ć]An9K$t딣m{!) G~Nq6A.,P"LXUL6:*e(-ή>hPrC̼k\|aڐض fچf+kXOغiy ͍vYuj)aQni3:ZUIIko2y9k 0ەԐi830;3yHJb Q__0pEW8SXT',m}PWnW^r#$  =D Te.bط=;im9.8`_-l_Yzj7-~' 9/Fh3Ov#b pVY倣gs7T[ -@rq \p[8$e5춥[BBɶpnj`[)w4v&{Jp%[3W·KUv@cyrN `~D9~&#{ i* !ɣX:,Z73\]tU :46b8?irLn \ropTirGq:Qnt|Nz`1*[FcEl.!~YEue(+Æ5 SoC5Ą3i;1% o~?ePnR39>1m7v@m(UDɀcπhS('! 76K˔!yI}ueCj+fO *9DE uv+tl^^~og:`p?U;Cl2 *b$$jkEY)ֶK՜I/"nxɻn23=C& NYw}!|NHFzIP_/vAy'*X0;=BpƬ/f.R|[Ï= J){Sډ;6"Q4,0CL ۟QKMuTW#>U5}Ȑɶy̢rmA'$}kf!@dq I2aمS,I3́E&50ؤ<([%.`@T\1՜,d9:1&N"'$pDPף 0l ̥ؕ pRhVhRĎO:!Evze|@JIXx*ru%\ `.9B/**9RyC+Ǹ1<̖&3 j1.]@$zqP8(/|(-A~m vW壪}hk5aGE.dž- 2 6peeiQs ?[V/S}=|C88݃7gi9ە& 05=@qDqB JpߓLO%T}1m;oۥ8DCr? vQB*uuJ0v{>%E ^H YTIY6sh{}mXAɄިh۾^-@C5njo"rO3:1|?[6p)7'O ~? _R*ή4@7Т@[7יh-(ijzh[?ä22;k]{%WaضuV 3;còC]:a˓ՋNP\Tʊ uzJ th@tzlSDZUL%_cۀ,y)ZaT$lB@F8S E FjU(;ZLAzV5q3.ꃉL 2(W[˙?WTbgl ' *IRl+?[;TL % ^CAW[%MfikqG]LjsrS8>B$t;)2щMyC؟ bA@w`7K;ԁGJNr !_}ꐑV@iIwՏ[>he)g/>y@`点_ι2XNʇimVnkZSV򘣏D@t꜅A[ 55a=ϧ¿-V¡]Wy|$frI=rԳ<W;Wl4Z](6c~Ds`'ad'rS['a1›8RKtG w 0g{p`>|zqaJ%zf7:'3.etw~i/K:ҳ_ZVQmUQ4BZa}1VlD@K V\9#b=1 NUc ~-X&_aG|XS y.ÅI]8b^R_yb  KWavTS|PPWo,;b]6K ~U(8Td+m>w 慨J0eYi83uUxPY66cևlw>PUQ缧ieظ@^йG&Λq?`Ӛ_ymnY[IK;z%O<ϼ~x'eбs:'[zJ1dKJe3SH +?3˲wOvl2;I;opBte%x[|u6gR'o: 0pOu5irA?nϋtACFhCGȔPoG//*7;w鮖K|>󠛜=Cɸ/2P5c"|l߲?, of.>PھqMt9F3rA];'wĨ$AV qy5X0+T`^-Ias'{Z WY盫|>Չ;]qjw׊&8}-{k6[jfF?V)xr1}~Vg9Je3f%;(ˊR=oL63}Čaƌ˜- dAK)Z,KY-\YJ-y(;xWV+]AI%[JA"uS%[>Ax zZib Oĕg"oCt |>/A!J>:s*J3vmb<|=`Wu"KAR;*UwyXWUЮ7ן ,S.lnŏR{swG2۳/j'* U:{ /`ŒVK\kޟy̷f,"ظD3Xe2Ӿ >x F9qͯxQYQüPW[%*A#͕ |`h.LaGOlZ;JV+ߏEs?1W hrVgQ6 oT fOGEy62?~91O7'bo.IlpD?{ˊMAw)+8g3%Kgӏ@e~KL20 pmq="Za$γ>MC q+G/f q~t?mŊ+\\v=XroOqMhmgpUk]|{8p2' ?)O&åxZSm&#B6@T>V#P@_ͩж*eȇjjAnܥ:}kB3Ȇ СOܷ~u@4`u: uvXZ\J8f< !>5vV :͊-!1w5E'߈n&AqIkˊ"ď&қ>tIvT`{nG (+Ea])* R_Zk0gƳB!cN6 r]2W/a`Aqؽc*JPRMkbWAހ9D 6.|57a_h[hxO:z{ &oX'Ǟɬ"ܵmV.]8+H"q/ؼW}z'ܝjƆz,]4X _s3sv9Oތ[>vMjEVW`ê%}Xd>޼]Xt>>yI>@?u!*ʊgfS0 VW {sL{f| .r$v]عm=jO :&5ٵ>ˍN]{ 6r1Xw(/-ž[܏P_mÂ79aG3mV3 kPW[%?~Xx<^VcϮmX7|4]e8efu7_ƆzbْՕؼa%1 /?y+qochc0ty=QQ(.֍Q_W11xPWW=۱nol4}ֳ/&&}Ţm?. wn7*MؓsgNǣ\*Ɠ7.bέXzณFVמ,C5*o|> FL13T,ۧg`'o%ߣ !ݍߊߟۍ.]{IpE6QQrt Es ˍXx!= ?QT=wnBkty9{q466>DyY \.7JKK뒟^@Rr P Os8,m rsvcl|:]r۷n׳>]߀wޜR?NkW_|:e%p\(+-Kw՗EbR"bbbQ__cɧKtWzrdjku[2rO#S!72%JD,S ^cCF1?66wJ; (Fay|b55( /R^֮ B6O/ܷl -ێ B$.в  }lLBuZ84{]+N6_#gOɖim[[ǩyŸ4]١u͵i5/^%e!hh><q'4ИA%N[^ׁPDC%J>gց>: P*)QI8"c~-XCC'#[!iCǚҍ?T`@88mӱeuyp\,^mP\poJ76I8)قE^^ ˆO<#wf376> Y=0B g( TfH*"c ~v6zfl^+|,__3mZaaKMCF;FEz%`\yضy-6_x!yyTcňWCw^",Bsܸɸ :際 >}-pxJL_Bn0'CS0㝗0㝗#ǝ[ys7l;˰h,\3J SN@fO"Xb)V 6ȷ{©x7ao)hl9xx磹|E(؛gaY ˅K.S|N\rΛ];R;oEw7.<ċ؀xo23` vUX>QQxxx7PVVW_zT~\\󨬨7sDccze:;%uACk֔z"V{XV3^kZ~.a1B8F`hCT1 Kosʥbs0pddvq']T:D&-# s~q7!ʊQ[]0.WQ^"tGظD䠾:uÈ1[@3L\zH 奅(/ |Qq-O3x==q@~B}]ɻs>p6z|`h6r>TWڒ!#©]zxdTt,6bPY^/q'q',`;|9x~TW1[c0tQ{^B!4_z18f9ODEY1ʩxѥ{_;lLtl5tG|晧gtι?0 =z` wώ퇌A5ZZFEGIg#>! e%(+eڣ/t6?,*Gue9:eucO@z4exu}z'$Kޘ0LOrux B>-M: .&$&&%ѻ?N8Y-܎t\.7rQ]^otSϼG3Q8Ec^yԸ0iڽJP7>hzZ5s⢽(+-AV8z Iuз;e/GrTTUVp=-=ǟ/<8 2$tCH.z˯CBG@iۖEn=p e΀cOS`eef~llF6Z<[/evt J?Sϼi\. ?d$F6F$shpeRۙDI FuL ˮqqqhPBmmx0ZW f!EO3@MBaB %)-Ǻ1¦Ϯ|cCkʖBb? l.mъiylNY|}d ]ql]sZYfhLk`Ĵz-WqTm+1"0걷ڦ,k<7/rDˤiZfWLAnpTyM&D%[6߈(ní+#h*ZT0 iSLr}M܁,'׀cPSUdUNq<P*$0Nꐯ UOAT[ha-r $YOy.7ljjDUE)SuH;7Ԉ2=' wXHDrG];p˯ xpem![(ڬI4%|> ?9O5#M|]g` BnKԲiY:g3j/60Ķ %gn0'bmC#̕JPrUިE[$97PFLZFgX妾e2lmU0P]jej+"WUVLku>̼HyF$AhmaZ?a.qchrv Xڴ=!<;CߵB}rrH%B̓<F;dkpx7&'G&'jƼ\FU4V6a#Mx+ԥ& Eӌʱ \;tkL'a::҄o I6\.ײ]VЖ?I`p?>n5#-[$L|Y;m捐ٶ&֦OuR iOؗN4zéG3T":S-$OV/lUa|pm AN%W^vpPUDL]rtS f8 fhl(ڻ]>Z*SQqdj4(x&tT*Ká*[#j/'<2SʴTftĖΩ\}TlZ-o@PvDPR 2x,oOI`iiiyCII\\Ai&qs)kiUjJKA.DZfUy)K9EKɬ)]xt1yPЪn̼T6IU c#V8(WXXghVcrhVz/Yr/8+x,Ԗl/C'mx7/ sG"T $jnnAOFhV`BH rfhRَFKy2G >vrNTH!K 94C$-?͐Vd1-?68e=+l*e Z٦!W*);T]ץHZ &î#LA b+S8EoMɈV)qujJВ2)'Zn?5Ɍwvi^p떚[C yÍ/Vx!493I}ZMU:;ILs܎pdogK"ٶ}L ^ 婅}\PE7}T2mVXj06F7ge211PT$v:zMfk]m-Bo߾wBTqqqai.{ e{PYd"-!`0r~V.Es~/68A|=_Gռ(\:j^ALiY7he֐.z+!6uTyVV$i' dd6H-ph9ay^Wռ o/"_8Zߏ s%a礦~7@#Gm1 v[!VAQc6G'jC-Ӣ)rPsAB~));uoHRr6bA:"ig+%3tRF5e8ݦz;wHʣ*ڱuf١Ud9udFZyy]!S ";;"i%%B;lHRu}Le%sZlId)"ZpZVaZP'Q"4jWm @[_E !S}|).àkc)K0ܜSIQIi%!}gf30jV_^SY72 9tDztkF{Ύ: {ְt8L+CCCv ==}/rY2&>A.en!GMP̲nHːoE꫑tEZ;j {oiPv;p$1DwοzV9"O'JP۩`[%u;eE0dv~@BvRs-Kg2ԑTMmA֖ H8vew#F³;h< Z7 ^[ķZ"Q԰6 IC}=Zz_i-\.׳ &* 3;>qxV-' 6n0gJn_Ja5粄( O - il0ezC)_"._1s%LNmL-I@무 v|/W85;[֞p7~y๬:  "=\+a%uש>60W+3C?&v2y:[-SeI+*/$[992[Uj W2EZ GAg ωLz"_#ft6}g'"иjC70hN|'fh_d-^*OM~$PL%OJ7aفwE6Icg~3m*º*HhM‗oCkͪRhs=_A8ҵUܿ6Pj9 _bbbqkOMMN~@.<2TΩ9܋":@)#Ҕҗ֏AXe29i=1GxL~>`MFsA&('SB!,(.qb)DFI _"SRM5V2;ֿXWa*w.(,:5#w19V[[ksBHa 03 QHKdcҫ-/4tCt![31@ːv0h:!lg*Է*[ȓ) &#]Uo Ѝ, ́>匃=*IDAT.Jad xH }n,L\(TTG kuY/[#AT vHm5oe%˧T+%"]V ~ ʕtT9T2uU)'S!WZ\_!L2z"\leUd G qbc0înH*}”i5Ol$aȨkjb#;cو!BrA@HopC!q@ n(B $7?U n^{=35ϛ鮮~3c+iUUUWv?-q3 m2[*GQF,jSLLŪ&GsYZev$cyl-i͓mDp)8].!e9:88h48ֶ ؔ7p+o=fk7-{~C7_n.QIJ2=< ModL6@S͓O8K2{Erz**)OӬHN8toܨ$>N/JoS)ETٔvei+o]kQٛms}S?R _QUU~ +C}*82da Upљ iU!dNo8' y=n̦*E,Zg :vSNM;uTϜ t$" H>>j~钋rY7cky| 9E0%̙XljKK̶>ͨ`4%x¸i3QWlcPjW潜BLC&S`C3%/Z(Y*8NjӎNrw8UF2]R'r*i'D0}n#|rx̥^q}d'{i~J03e"D *I$GFY%7"<>R 1|`,Q7[^P!%rbQQsJaiPNa̎dmOKfEƳ9_ASc6w2}t|giGs”. 7ixoQ4"vxxݰj9K P+++op፯=wjsHcg<٫B,5I\OJԆ6PkܝA% ̧V._LEN՜؃O T mIo{nqt{}c3?CL~Tܺs#^[QiOg TէsN ̙.*>ݬy\\[6쳀'_+gĵ޺5ڜ!g9;zx:W^*yxx`Ii7Ο?3g9??Ï1873iuIթ%RUߌdK${4<$\1rOƣws?pF1CKŪfkA_Ħ#D?`dJǣ4|D[*]x+++p뿞^x.<%ssb\>+q2Q6;\ͦ#ns"ǂ l)w#2 &g"}9lؼLIrq +͹;LrІtj`rUХ8ʊ*a!äIpj<+'wz9W|+5>?ģOPձvwTeufvh=aXF<-IuC0siExz9AY%cP3ts/:^)[9'/@]gk>0s-ąċ7z.-)a\4;xMH)Q{ax 뱩J#s/~n_ec?m@xMVדsc֦C ^\xk0#UU}ٳOHvwwA ]c~O]|9x7Wzt-{a2 94%P\\JT wF1r<{JCzRf9Ĥ6A3jIĜ#xMvx>!f$bF|\$Esuu̥M{UU8v 뽿wss76z1[W+xiW`٧ b@.tT}\SE0iK؊qM*Ě`,QtZh>.HPWԤb"e7$'Vm~kFV\:^AnT}s̥XTd.o] ϵ;ȴ(/_ a嗊)R./Mnxs+qC7k5LŢ}Udi?S6mblJGOel1 /]sJڎ| Z06s [Wn&~O Y.GrSfVQ cE\pRYUË+d#(!jw|H ldy@$q-%c[G):-PSXr`Icܘ?C̪10Czh<?p7_)1`ggg8> ` =cJW==4CvEDm7=eH2U\[.9a,J0'󝡪((b{2Cnzx?NnД3lmxS&r 0~Yi ;| J^ UaӔ[ܳv$޷}̤V/ǎmF\[ϼsOvh&Mš-e6V{ \nȯp}d>,osoWUPUcBz'[s0 Q{c8p<Ƹ/>Iv%GaOFkIďzGgkg&YChٳ(du<$!Ede$p1qͫ0c@sӍnǕ9|cVDțiL2 vy#F]_$=Ql&q >qy]\&6 ӼG]ר\Jo^sP`RܒC̆._|[UUq7+訣:ꨣ:ꨣ:ԒOιo 'Lw騣:zιoptZb6t…p/7A:ꨣ:ꨣ:ꨣN7=ιߝ1mhggg pȫuQG-jK܏(3hVw{oZnB:ꨣ:ꨣ:ꨣ@5\q=Ucι?zlllH4]ιGp3'kVGuѩܞ~ ιVVV^__(ĥ hIENDB`fortran-toml-0.5.0/doc/_static/img/jonquil.svg0000664000175000017500000022201315201541453021475 0ustar alastairalastair fortran-toml-0.5.0/doc/_static/css/0000775000175000017500000000000015201541453017307 5ustar alastairalastairfortran-toml-0.5.0/doc/_static/css/custom.css0000664000175000017500000000223715201541453021337 0ustar alastairalastair.ansi-block { background-color: #fafafa; color: #000000; } .ansi-bold { font-weight: bold; } .ansi-black { color: #000000; } .ansi-red { color: #b21717; } .ansi-green { color: #17b217; } .ansi-yellow { color: #b26717; } .ansi-blue { color: #1717b2; } .ansi-magenta { color: #b217b2; } .ansi-cyan { color: #17b2b2; } .ansi-white { color: #b2b2b2; } .ansi-black.ansi-bold { color: #686868; } .ansi-red.ansi-bold { color: #ff5454; } .ansi-green.ansi-bold { color: #54ff54; } .ansi-yellow.ansi-bold { color: #ffff54; } .ansi-blue.ansi-bold { color: #5454ff; } .ansi-magenta.ansi-bold { color: #ff54ff; } .ansi-cyan.ansi-bold { color: #54ffff; } .ansi-white.ansi-bold { color: #ffffff; } .ansi-bg_black { background-color: #000000; } .ansi-bg_red { background-color: #b21717; } .ansi-bg_green { background-color: #17b217; } .ansi-bg_yellow { background-color: #b26717; } .ansi-bg_blue { background-color: #1717b2; } .ansi-bg_magenta { background-color: #b217b2; } .ansi-bg_cyan { background-color: #17b2b2; } .ansi-bg_white { background-color: #b2b2b2; } fortran-toml-0.5.0/doc/_static/header.svg0000664000175000017500000001570715201541453020502 0ustar alastairalastair fortran-toml-0.5.0/doc/locales/0000775000175000017500000000000015201541453016513 5ustar alastairalastairfortran-toml-0.5.0/doc/locales/de/0000775000175000017500000000000015201541453017103 5ustar alastairalastairfortran-toml-0.5.0/doc/locales/de/LC_MESSAGES/0000775000175000017500000000000015201541453020670 5ustar alastairalastairfortran-toml-0.5.0/doc/locales/de/LC_MESSAGES/tutorial.po0000664000175000017500000014634415201541453023107 0ustar alastairalastair# Copyright (C) 2019-2022, Sebastian Ehlert # This file is distributed under the same license as the toml-f package. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: toml-f 0.2.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-30 16:12+0200\n" "PO-Revision-Date: 2022-07-30 16:12+0200\n" "Last-Translator: Sebastian Ehlert\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.10.3\n" #: ../tutorial/fpm-lint.rst:2 msgid "Building a linter" msgstr "" msgid "Difficulty: Beginner" msgstr "Schwierigkeit: Anfänger" #: ../tutorial/fpm-lint.rst:8 msgid "" "This tutorial will show how to use TOML Fortran to build a linter for " "your configuration files. Linters provide a way to encourage or enforce a" " certain style or flag up common usage errors." msgstr "" "Dieses Tutorial zeigt, wie du TOML Fortran zum Erstellen eines Linters " "für deine Konfigurationsdateien verwenden kannst. Linter bieten eine " "Möglichkeit, einen bestimmten Stil zu bevorzugen oder die häufig " "gemachten Fehler zu finden." #: ../tutorial/fpm-lint.rst:13 msgid "Target selection" msgstr "Zielauswahl" #: ../tutorial/fpm-lint.rst:15 msgid "" "This tutorial will look into finding lint in the package manifest from " "the Fortran package manager (`fpm `_). " "We will use its plugin mechanism to create a new subcommand called ``lint``." msgstr "" "Dieses Tutorial wird auf die Suche nach Lint in dem Paketmanifest des " "Fortran-Paketmanagers (`fpm `_) eingehen. " "Wir werden den Plugin-Mechanismus des Fortran-Paketmanagers verwenden, " "um ein neues Unterprogramm mit dem Namen ``lint`` zu erstellen." #: ../tutorial/fpm-lint.rst:20 msgid "We start with setting up the package manifest for our linter:" msgstr "Wir beginnen mit der Einrichtung des Paketmanifests für unseren " "Linter:" #: ../tutorial/fpm-lint.rst:22 ../tutorial/fpm-lint.rst:38 #: ../tutorial/fpm-lint.rst:91 ../tutorial/fpm-lint.rst:134 #: ../tutorial/fpm-lint.rst:196 ../tutorial/fpm-lint.rst:280 #: ../tutorial/getting-started.rst:14 msgid "fpm.toml" msgstr "" #: ../tutorial/fpm-lint.rst:33 msgid "Configuration of the linter" msgstr "Konfiguration des Linters" #: ../tutorial/fpm-lint.rst:35 msgid "" "To configure our linter we will use the `extra section `__ in" " the manifest which is specially reserved for tools integrating with fpm " "and boldly claim *extra.fpm.lint* as our configuration section. Using the" " package manifest provides us with two advantages, first this document " "will be present in all projects using fpm, second if we can read our " "configuration from the manifest, we are already sure it is valid TOML." msgstr "" "Um den Linter zu konfigurieren, werden wir die `extra-Sektion `__ " "im Manifest verwenden, welche speziell für Tools, die mit fpm " "integriert werden, reserviert ist und *extra.fpm.lint* als " "Konfigurationssektion verwendet. Durch das Paketmanifest erhalten wir " "zwei Vorteile, erstens ist dieses Dokument in allen Projekten, die " "fpm verwenden, vorhanden, zweitens wenn wir unsere Konfiguration " "aus dem Manifest lesen können, ist sichergestellt, dass sie gültiges " "TOML ist." #: ../tutorial/fpm-lint.rst:46 msgid "Now we will set up our main program to run the linter." msgstr "Als nächstes setzen wir das Hauptprogramm, um den Linter zu " "starten." #: ../tutorial/fpm-lint.rst:48 ../tutorial/fpm-lint.rst:99 #: ../tutorial/fpm-lint.rst:160 ../tutorial/fpm-lint.rst:208 #: ../tutorial/fpm-lint.rst:262 msgid "app/main.f90" msgstr "" #: ../tutorial/fpm-lint.rst:52 msgid "" "We create a utility module for the *get_argument* function used to " "retrieve the manifest name, in most cases we can default to *fpm.toml*, " "but for testing it is convenient to pass an argument." msgstr "" "Wir erstellen ein Utility-Modul für die *get_argument*-Funktion, " "die zum Abrufen des Manifest-Namens verwendet wird. In den meisten " "Fällen können wir diesen per Default auf *fpm.toml* setzen, aber für " "Testzwecke ist es nützlich, ein Argument übergeben zu können." #: ../tutorial/fpm-lint.rst:54 ../tutorial/fpm-lint.rst:253 msgid "src/utils.f90" msgstr "" #: ../tutorial/fpm-lint.rst:59 msgid "" "The first error source we can encounter stems from parsing the TOML " "document itself. This is outside of our responsibility to handle, still " "we want to check whether we can report the error correctly." msgstr "" "Die erste Fehlerquelle, die wir bei der Parseroutine selbst " "erkennen können, ist das Parsen des TOML-Dokuments. Dies ist " "außerhalb der Verantwortung des Lint-Programms, trotzdem wollen wir " "prüfen, ob wir den Fehler korrekt melden können." #: ../tutorial/fpm-lint.rst:62 msgid "fpm.toml (invalid)" msgstr "fpm.toml (ungültig)" #: ../tutorial/fpm-lint.rst:66 msgid "" "Running the linter on this document will break with the following message" " produced by the *toml_load* procedure." msgstr "" "Durch das Starten des Lint-Programms auf diesem Dokument wird die " "folgende Fehlermeldung produziert, die von der *toml_load*-Prozedur " "ausgegeben wird." #: ../tutorial/fpm-lint.rst:78 msgid "" "With this case covered we proceed with reading the configuration for our " "linter." msgstr "" "Mit diesem Fall abgeschlossen setzen wir uns an, die Konfiguration " "für den Linter zu lesen." #: ../tutorial/fpm-lint.rst:80 msgid "" "Our configuration from the package manifest will be stored in a " "*lint_config* type which we define in a separate module. Reading the " "configuration will happen from the root table, meaning we have to advance" " through several subtables first before we can process the options for " "our linter. We want to report errors with rich context information here " "as well, therefore we request the *origin* in every call to the " "*get_value* interface and produce a report using the *context* we " "obtained in the main program." msgstr "" "Unsere Konfiguration aus dem Paketmanifest wird in einem " "*lint_config*-Typ gespeichert, welcher in einem separate Modul " "definiert wird. Die Konfiguration wird von der Wurzel-Tabelle " "gelesen, d.h. wir müssen zunächst durch mehrere Untertabellen " "vorgehen, bevor wir die Optionen für den Linter verarbeiten können. " "Wir möchten hier Fehlermeldungen mit ausführlicher Kontextinformation " "erhalten, daher werden die *origin*-Angaben in den Aufrufen der " "*get_value*-Schnittstelle benötigt und wir erzeugen einen " "Bericht mit Hilfe des *context*-Wertes, den wir im " "Hauptprogramm erhalten haben." #: ../tutorial/fpm-lint.rst:84 msgid "src/config.f90" msgstr "" #: ../tutorial/fpm-lint.rst:88 msgid "" "For convenience, we defined a *make_error* routine to allocate the error " "handler and store our report from the context. At this point, we should " "check whether our error reporting works and run the linter on an " "incorrect TOML document." msgstr "" "Für einen einfachen Zugriff definiert wir eine *make_error*-Routine, " "die den Fehlerbehandlungsprozess ermöglicht und den Bericht aus dem " "Kontext speichert. An dieser Stelle sollten wir prüfen, ob die " "Fehlermeldungen korrekt funktionieren und den Linter auf einem " "falschen TOML-Dokument starten." #: ../tutorial/fpm-lint.rst msgid "current main program" msgstr "aktuelles Hauptprogramm" #: ../tutorial/fpm-lint.rst:97 ../tutorial/fpm-lint.rst:158 #: ../tutorial/fpm-lint.rst:260 msgid "Putting everything together in the main program should look like this." msgstr "Das Hauptprogramm sollte wie folgt aussehen." #: ../tutorial/fpm-lint.rst:103 msgid "" "Running our linter on this file will correctly flag this as an error " "since a string value is provided rather than a boolean value." msgstr "" "Durch das Starten des Lint-Programms auf diesem Dokument wird dies " "als Fehlermeldung markiert, da ein String-Wert anstatt einer " "Boolesche-Angabe angegeben wurde." #: ../tutorial/fpm-lint.rst:115 msgid "" "Finally, we define a logging mechanism to capture our actual linting " "messages which are not fatal. The logger provides two procedures, " "*add_message* to store a message and *show_log* to display all stored " "messages." msgstr "" "Zuletzt definieren wir ein Logging-Mechanismus, um die tatsächlichen " "Lint-Meldungen zu speichern, die nicht fatal sind. Der Logger " "bietet zwei Prozeduren, *add_message* zum Speichern einer " "Meldung und *show_log* zum Anzeigen aller gespeicherten Meldungen." #: ../tutorial/fpm-lint.rst:118 msgid "src/logger.f90" msgstr "" #: ../tutorial/fpm-lint.rst:124 msgid "Recommended package name" msgstr "Empfohlener Paketname" #: ../tutorial/fpm-lint.rst:126 msgid "" "As a first linting check we will inspect the package name, for this we " "will apply the following rules:" msgstr "" "Als erste Prüfung werden wir den Paketnamen prüfen, dazu werden " "folgende Regeln angewendet:" #: ../tutorial/fpm-lint.rst:128 msgid "" "the package name should be a TOML bare key to not require quotes in " "*dependency* sections, characters like dots, colons, or slashes are not " "allowed" msgstr "" "der Paketname sollte ein TOML-Bare-Schlüssel sein, um in den " "*dependency*-Bereichen keine Anführungszeichen zu erfordern, " "Sonderzeichen wie Punkte, Doppelpunkte, Kommas oder Schrägstriche " "sind nicht erlaubt" #: ../tutorial/fpm-lint.rst:129 msgid "" "TOML generally favors lowercase dashed keys, therefore we will discourage" " capitalization (camelCase and PascalCase) as well as underscores " "(snake_case)" msgstr "" "TOML bevorzugt mit Bindestrichen getrennte Kleinbuchstaben in Schlüsselnamen, " "daher werden wir Großschreibung (camelCase und PascalCase) und " "Unterstriche (snake_case) verbieten" #: ../tutorial/fpm-lint.rst:130 msgid "" "there are several ways to declare strings in TOML, we want to favor the " "normal string one" msgstr "" "Es gibt mehrere Möglichkeiten, Strings in TOML zu deklarieren, wir " "möchten das normale String-Format vorziehen" #: ../tutorial/fpm-lint.rst:132 msgid "" "An example of a package name we would disallow would be *fpmLinter* as " "seen in the manifest below." msgstr "" "Ein Beispiel eines Paketnamen, den wir verboten werden würden, wäre " "*fpmLinter* wie im Manifest unten angezeigt." #: ../tutorial/fpm-lint.rst:138 msgid "" "Let's start with our implementation of this check. For convenience we " "will reexport the other modules from the *fpm_lint* module, this allows " "one clean import in the main program. Then we define the *lint_data* " "procedure, where we first check whether the *name* key is present, if not" " we create a message at the *info* level and leave our block scope, as " "all further checks rely on the presence of the entry." msgstr "" "Damit wir mit unserer Implementierung beginnen können, reexportieren " "wir die anderen Module aus dem *fpm_lint*-Modul, dies ermöglicht einen " "klaren Import im Hauptprogramm. Anschließend definieren wir die " "*lint_data*-Prozedur, in der wir prüfen, ob der *name*-Schlüssel " "vorhanden ist, wenn nicht erstellen wir eine Meldung im *info*-Level " "und verlassen den Block-Scope, da alle weiteren Prüfungen auf die " "Vorhandensein des Eintrags basieren." #: ../tutorial/fpm-lint.rst:142 msgid "" "We can now check whether the entry is provided as a string or maybe as " "something else, like a literal string, which we can flag. Furthermore, we" " verify that the package name uses only lowercase letters, numbers, and " "dashes with the *verify* intrinsic." msgstr "" "Wir können jetzt prüfen, ob der Eintrag als String angegeben ist oder " "möglicherweise als etwas anderes, wie ein Literalstring, das geprüft " "werden muss. Weiterhin prüfen wir, ob der Paketname nur Kleinbuchstaben, " "Zahlen und Bindestriche verwendet." #: ../tutorial/fpm-lint.rst:145 ../tutorial/fpm-lint.rst:227 #: ../tutorial/fpm-lint.rst:243 msgid "src/lint.f90" msgstr "" #: ../tutorial/fpm-lint.rst:152 msgid "" "The ``toml_level`` parameter provides a statically initialized derived " "type enumerating all available report levels. Similarly, the " "``token_kind`` parameter provides an enumeration of the token kinds. You " "can think of it as an enumerator with a proper namespace." msgstr "" "Der ``toml_level``-Parameter stellt einen statisch initialisierten " "abgeleiteten Typ dar, der alle verfügbaren Meldungslevels enthält. " "Ähnlich gilt der ``token_kind``-Parameter als Enumerator mit einem " "gültigen Namensraum." #: ../tutorial/fpm-lint.rst:164 msgid "" "We check this on the camelCase package name from above and can find the " "following output." msgstr "" "Wir prüfen diesen auf den Paketnamen mit camelCase von oben und " "können folgende Ausgabe finden." #: ../tutorial/fpm-lint.rst:177 ../tutorial/fpm-lint.rst:297 #: ../tutorial/fpm-lint.rst:315 ../tutorial/json.rst:192 #: ../tutorial/json.rst:279 msgid "Exercise" msgstr "Übung" #: ../tutorial/fpm-lint.rst:180 msgid "" "Add a check for the length of the package name, everything under three " "characters is probably a bad choice, so is a too long package name." msgstr "" "Füge eine Prüfung für die Länge des Paketnamens hinzu, alles unter " "drei Zeichen ist wahrscheinlich ein schlechter Wahl, genauso wie ein " "zu langer Paketname." #: ../tutorial/fpm-lint.rst:182 msgid "" "Create an example to trigger the error with your new check. What happens " "if a too long camelCase package name is used?" msgstr "" "Erzeuge einen Beispiel, um den Fehler auszulösen. Was passiert wen ein " "zu langer camelCase-Paketname verwendet wird?" #: ../tutorial/fpm-lint.rst:187 msgid "Bare key paths preferred" msgstr "Blanke Schlüsselpfade bevorzugt" #: ../tutorial/fpm-lint.rst:189 msgid "" "TOML allows to quote keys, however this might become visually distracting" " if some keys are quoted and others are not. With our package name rule, " "there should not be the need to quote any keys even in dependency " "sections." msgstr "" "TOML erlaubt Schlüssel zu quotieren, was aber visuell unübersichtlich " "wird wenn nur einige Schlüssel quotiert werden und andere nicht. " "Mit unserer Paketnamenregel sollte es nicht notwendig sein, irgendeinen " "Schlüssel in Abhängigkeitsabschnitten zu quotieren." #: ../tutorial/fpm-lint.rst:192 msgid "" "To determine whether a string is used in the context of a key we need a " "way to identify all keys. We could check all entries in the data " "structures by implementing a visitor object which walks through all " "tables and checks the keys. However, this is somewhat inefficient and we " "can also miss keys that are not recorded." msgstr "" "Um zu bestimmen, ob ein String in dem Kontext eines Schlüssels verwendet " "wird, müssen wir eine Methode finden, um alle Schlüssel zu identifizieren. " "Wir können alle Einträge in den Datenstrukturen durchlaufen und die " "Schlüssel prüfen. Allerdings ist dies auch etwas teuer und wir können " "auch Schlüssel verpassen, die nicht aufgezeichnet werden." #: ../tutorial/fpm-lint.rst:200 msgid "" "In this example, the second occurrence of the key ``toml-f`` will only " "reference the table but it is already defined the line before. The " "quotation marks are visually identifiable as lint and we need a " "programmatic way to flag this." msgstr "" "In diesem Beispiel wird das zweite Vorkommnisse des Schlüssels ``toml-f`` " "nur referenzieren, aber es ist bereits in der Zeile vorher definiert. " "Die Anführungszeichen sind visuell identifizierbar als Lint und wir " "müssen eine Programmiermethode finden, um dies zu markieren." #: ../tutorial/fpm-lint.rst:203 msgid "" "Instead of working with the data structure, we will use the parser to " "record more tokens in the context. Rather than using the context to only " "report errors, we will use it to identify keys. This is done by " "increasing the *context_detail* option in the *config* keyword of the " "parser to one. Now all tokens except for whitespace and comments will be " "recorded." msgstr "" "Anstatt mit der Datenstruktur zu arbeiten, werden wir den Parser verwenden " "um mehr Tokens in den Kontext zu speichern. Anstatt nur Fehler zu " "melden, werden wir den Kontext verwenden, um Schlüssel zu identifizieren. " "Dies wird durch das Erhöhen der *context_detail*-Option in der " "*config*-Schlüssel der Parser auf eins gesetzt. Damit werden alle " "Tokens, außer Leerzeichen und Kommentare gespeichert." #: ../tutorial/fpm-lint.rst:216 msgid "" "Increasing the ``context_detail`` to two will also record whitespace and " "comments. This can be useful when writing checks for whitespace or " "indentation styles." msgstr "" "Durch das Erhöhen der *context_detail* auf zwei werden auch Leerzeichen " "und Kommentare gespeichert. Dies kann hilfreich sein, wenn Checks für " "Leerzeichen oder Einrückungsstile geschrieben werden." #: ../tutorial/fpm-lint.rst:219 msgid "Our linter pass will work as follows:" msgstr "Unsere Linter-Schritt läuft wie folgt:" #: ../tutorial/fpm-lint.rst:221 msgid "identifying all relevant keys in the manifest" msgstr "identifiziere alle relevanten Schlüssel im Manifest" #: ../tutorial/fpm-lint.rst:222 msgid "check whether they are keypath tokens" msgstr "prüfe, ob sie Schlüsselpfad-Tokens sind" #: ../tutorial/fpm-lint.rst:223 msgid "create a report for any key that is a string or a literal" msgstr "erzeuge einen Bericht für jeden Schlüssel, der ein String oder " "ein Literal ist" #: ../tutorial/fpm-lint.rst:225 msgid "" "Our implementation reflects this by first collecting an array of " "*toml_key* objects in *list* and then iterating over all entries checking" " whether they are the correct *token_kind*." msgstr "" "Unsere Implementierung reflektiert dies durch das Sammeln einer Liste von " "*toml_key*-Objekten in *list* und dann durchlaufen aller Einträge, " "prüfen, ob sie das korrekte *token_kind* haben." #: ../tutorial/fpm-lint.rst:232 msgid "" "To create the list we need to implement the *identify_keys* procedure. " "The rules in TOML for key paths are simple: before an equal sign we can " "have key paths and keypath can only be present in table bodies or inline " "tables. This can be implemented by using a stack storing whether the " "current scope belongs in a table, array, or value." msgstr "" "Um die Liste zu erzeugen müssen wir die *identify_keys*-Prozedur " "implementieren. Die Regeln in TOML für Schlüsselpfade sind einfach: " "vor einem Gleichheitszeichen können Schlüsselpfade sein und " "Schlüsselpfade können nur in Tabellen und Inline-Tabelle vorkommen. " "Dies kann implementiert werden, indem ein Stapel verwendet wird, " "um zu prüfen, ob der aktuelle Abschnitt in einer Tabelle, einem " "Feld oder einem Wert ist." #: ../tutorial/fpm-lint.rst:236 msgid "" "We will always push a new scope on the respective token opening it, " "*i.e.* a value is opened by an equal sign, an array by a right bracket, " "and an inline table by a right curly brace. To distinguish table headers " "from inline arrays we only push arrays on our stack after an equal sign. " "Finally, we default to a table scope if no other scope is present and we " "have collected all required rules to identify key paths. Similarly, we " "can identify the endings of the scopes." msgstr "" "Wir werden immer einen neuen Abschnitt hinzufügen wenn wir das Token " "finden, das es öffnet, d.h. ein Wert öffnet sich mit einem Gleichheitszeichen, " "ein Feld mit einem rechten Klammer-Symbol, eine Inline-Tabelle " "mit einem rechten Klammer-Symbol. Um Tabelle-Überschriften von " "Inline-Arrays zu unterscheiden, fügen wir nur Arrays auf den Stapel " "hinzu nach einem Gleichheitszeichen. Zuletzt setzen wir den ersten Abschnitt " "als einen Tabelle-Abschnitt, wenn kein anderer Abschnitt vorhanden ist " "und wir haben alle benötigten Regeln zum Identifizieren von Schlüsselpfaden " "gesammelt. Analog gilt es für die Endungen der Abschnitte." #: ../tutorial/fpm-lint.rst:241 msgid "" "We then can check whether the current scope on the top of the stack " "allows key paths and record those in our list." msgstr "" "Dann können wir prüfen, ob der aktuelle Abschnitt auf dem Stapel " "Schlüsselpfade erlaubt und diese in unsere Liste aufnehmen." #: ../tutorial/fpm-lint.rst:248 msgid "" "For convenience, we implement a *push_back* and *pop* function to add and" " remove scopes from our stack. The *pop* function will additionally " "perform a check whether we want to remove a matching scope and save us " "some repetition in the loop this way." msgstr "" "Für einen einfachen Zugriff implementieren wir eine *push_back*- und " "*pop*-Funktion, um Abschnitte auf den Stapel hinzuzufügen und " "entfernen zu können. Die *pop*-Funktion führt zusätzlich einen " "Prüfung aus, ob wir einen passenden Abschnitt entfernen möchten und " "vermeiden eine Wiederholung in der Schleife auf diese Weise." #: ../tutorial/fpm-lint.rst:251 msgid "" "In our utility module, we implement the *resize* procedure for an array " "of integers" msgstr "" "In unserem Utility-Modul implementieren wir die *resize*-Prozedur " "für ein Array von Ganzzahlen" #: ../tutorial/fpm-lint.rst:266 msgid "" "At this point, we can now add a call in our main program to run the " "linter." msgstr "" "In dieser Stelle können wir nun einen Aufruf in unserem Hauptprogramm " "für den Linter hinzufügen." #: ../tutorial/fpm-lint.rst:278 msgid "" "Now for something more tricky with an inline table to check whether our " "scoping rules are working correctly." msgstr "" "Jetzt für etwas schwieriges mit einer Inline-Tabelle, um zu prüfen, " "ob unsere Abschnittsregeln korrekt funktionieren." #: ../tutorial/fpm-lint.rst:284 msgid "" "Our linter can correctly identify the *tag* entry as a string in the key " "path context and produces the appropriate message." msgstr "" "Unsere Linter kann den *tag*-Eintrag korrekt als einen String in " "der Schlüsselpfad-Kontext identifizieren und produziert das " "korrekte Meldungsformat." #: ../tutorial/fpm-lint.rst:300 msgid "" "Previously, we flagged the usage of a literal string as a value for the " "package name, however a package manifest can contain much more string " "values." msgstr "" "Früher wurde die Verwendung eines Literal-Strings als Wert für den " "Paketnamen geprüft, aber ein Paketmanifest kann viel mehr " "Strings enthalten." #: ../tutorial/fpm-lint.rst:302 msgid "" "Create a check for all string values in the manifest to ensure they use " "double-quotes. Collect string values (*string*, *literal*, *mstring*, and" " *mliteral*) from array and value scopes for this purpose." msgstr "" "Erstelle einen Prüfung für alle String-Werte im Manifest, um sicherzustellen, " "dass sie mit Anführungszeichen versehen sind. Sammle String-Werte " "(*string*, *literal*, *mstring*, und *mliteral*) aus Array- und " "Wert-Abschnitten für diesen Zweck." #: ../tutorial/fpm-lint.rst:305 msgid "" "Can you make a meaningful suggestion if a literal string contains " "characters that must be escaped in a double-quoted string?" msgstr "" "Kannst du einen nützlichen Vorschlag machen, wenn ein Literal-String " "Zeichen enthält, das in einem String mit doppelten Anführungszeichen " "maskiert muss?" #: ../tutorial/fpm-lint.rst:309 ../tutorial/json.rst:349 msgid "Summary" msgstr "Zusammenfassung" #: ../tutorial/fpm-lint.rst:311 msgid "" "This concludes the linting we wanted to implement for the fpm package " "manifest. For a feature-complete linter, the rule set to check for is " "usually growing with time and might also shift as new rules are adopted. " "Our linter currently provides only a few rules but has the potential to " "include more checks as the need arises." msgstr "" "Dies beendet das Linten, das wir für das fpm-Paketmanifest " "implementiert haben. Für einen vollständigen Linter wird die Regelmenge, " "die geprüft werden soll, in der Regel mit Zeit weiterentwickelt und " "kann auch bei Bedarf auch wieder ändern. " "Unsere Linter bietet zur Zeit nur einige Regeln, aber es kann " "zusätzliche Prüfungen hinzugefügt werden, wenn der Bedarf entsteht." #: ../tutorial/fpm-lint.rst:317 msgid "" "Our output is currently in the order of the checks, rather than in the " "order of reports occurring in the TOML document. The output of the " "reports might become more intuitive if it was sorted according to the " "source lines." msgstr "" "Unsere Ausgabe ist derzeit in der Reihenfolge der Prüfungen, " "anstatt in der Reihenfolge der Meldungen, die in der TOML-Dokumentation " "auftreten. Die Ausgabe der Meldungen sollte intuitiverer " "darstellt sein, wenn sie nach den Quellzeilen sortiert werden." #: ../tutorial/fpm-lint.rst:320 msgid "" "Record the first character in the output together with the messages in " "the logger. Have the logger sort the messages according to their order " "before printing them." msgstr "" "Speichere die Position des ersten Zeichens in der Ausgabe zusammen mit den " "Meldungen im Logger. " "Der Logger sollte die Meldungen nach ihrer Reihenfolge vor der Ausgabe " "sortieren." #: ../tutorial/fpm-lint.rst:325 msgid "" "In this tutorial, you have learned how to report custom error messages in" " your TOML input data. You can now" msgstr "" "In diesem Tutorial hast du gelernt, eigene Meldungen in deinen " "TOML-Eingabedaten zu erstellen. Du kannst nun" #: ../tutorial/fpm-lint.rst:328 msgid "report colorized error messages with rich context information" msgstr "farbige Fehlermeldungen mit ausführlicher Kontextinformation " "ausgeben" #: ../tutorial/fpm-lint.rst:329 msgid "create error messages when reading a TOML data structure" msgstr "Fehlermeldungen erstellen, wenn eine TOML-Datenstruktur gelesen " "wird" #: ../tutorial/fpm-lint.rst:330 msgid "control the details captured in the context describing the TOML document" msgstr "die Details einstellen mit denen der Kontext des TOML-Dokumentes " "beschrieben wird" #: ../tutorial/fpm-lint.rst:331 msgid "check a TOML document based on the token information in the context" msgstr "ein TOML-Dokument prüfen, basierend auf den Tokeninformationen " "im Kontext" #: ../tutorial/getting-started.rst:2 msgid "Getting started" msgstr "Einführung" #: ../tutorial/getting-started.rst:8 msgid "" "This tutorial provides a gentle introduction to the use of TOML Fortran. " "It will deal with reading as well as creating TOML data structures using " "the high-level build interface and discuss how to obtain a data structure" " from a TOML document or turn a data structure to TOML document again." msgstr "" "Dieses Tutorial bietet eine einfache Einführung in die Benutzung von TOML" " Fortran. Das Lesen und Schreiben von TOML Datenstrukturen wird " "behandelt, sowie die Erstellung von Datenstrukturen aus TOML Dokumenten " "außerdem wird diskutiert wie eine Datenstruktur aus einem TOML Dokument " "erstellt wird oder wieder in ein TOML Dokument konvertiert wird." #: ../tutorial/getting-started.rst:11 msgid "" "For this project we will be working with fpm, however you can use any " "build tool you are familar with, checkout the :ref:`integration guide " "` to find a matching setup. We start with creating a minimal" " package manifest to use TOML Fortran in our fpm project." msgstr "" "Für dieses Projekt werden wir mit fpm arbeiten, aber Du kannst jedes " "Build-Tool verwenden, mit dem Du dich gut zurecht kommst, schau Dir die " ":ref:`Integrations-Anleitung ` an, um einen passenden Setup " "zu finden. Wir starten mit der Erstellung eines minimalen Paketmanifests," " um TOML Fortran in unserem fpm Projekt zu benutzen." #: ../tutorial/getting-started.rst:22 msgid "" "The public TOML Fortran API is defined in the ``tomlf`` module, we will " "only use this module for this entire course. The main data structures we " "are going to interact with are ``toml_table`` and ``toml_array`` " "instances, which we can conveniently manipulate with the generic " "interface ``get_value``." msgstr "" "Die öffentliche TOML Fortran API wird in dem Modul ``tomlf`` definiert, " "und wir werden nur dieses Modul für diesen gesamten Kurs brauchen. Die " "Hauptdatenstruktur, mit der wir uns in diesem Kurs konkret kommunizieren " "wird ``toml_table`` und ``toml_array`` Instanzen, die wir mit dem " "generischen interface ``get_value`` manipulieren können." #: ../tutorial/getting-started.rst:25 msgid "src/reader.f90" msgstr "" #: ../tutorial/getting-started.rst:29 msgid "" "Note that we declare the TOML data structure as mutable, *i.e.* " "``intent(inout)`` rather than just ``intent(in)``, as the ``get_value`` " "interface can modify the data structure. We start with a simple test " "program which is not actually reading any TOML document, but just passing" " an empty table to our reader." msgstr "" "Hinweis: Wir deklarieren die TOML Datenstruktur als mutable, *d.h.* " "``intent(inout)`` statt nur ``intent(in)``, da das ``get_value`` " "interface die Datenstruktur verändern kann. Wir beginnen mit einem " "einfachen Testprogramm, welches nicht einmal ein TOML Dokument liest, " "sondern nur eine leere Tabelle an den Reader weitergeben." #: ../tutorial/getting-started.rst:32 msgid "app/defaults.f90" msgstr "" #: ../tutorial/getting-started.rst:36 msgid "" "The ``get_value`` interface for processing the TOML data structure " "ensures that the data structure is complete throughout the whole process " "of reading it and will add the requested nodes if there are not present " "or will fill them in with default values. Convince yourself that the " "empty table indeed changed while reading by passing a serializer to it." msgstr "" "Das ``get_value`` interface für die Verarbeitung der TOML Datenstruktur " "stellt sicher, dass die Datenstruktur vollständig durch den gesamten " "Prozess gelesen wird und fügt die gewünschten Knoten hinzu, wenn sie " "nicht vorhanden sind oder füllt sie mit Standardwerten aus. Überzeuge " "Dich, dass die leere Tabelle während des Lesens wirklich geändert wurde, " "indem Du einen Serializer an die Datenstruktur übergibst." #: ../tutorial/getting-started.rst:55 msgid "" "This behavior is very convenient because it allows us to define our " "default values while defining how we read the TOML data structure." msgstr "" "Dieses Verhalten ist sehr einfach, da wir unsere Standardwerte definieren" " können, während wir definieren, wie wir die TOML Datenstruktur lesen." #: ../tutorial/getting-started.rst:59 msgid "" "The ``get_value`` build interface is only one way of accessing the TOML " "data structure provided by TOML Fortran. It takes an opinionated approach" " towards reading and modifying the data structure, which is suitable the " "majority of applications." msgstr "" "Das ``get_value`` build interface ist nur eine der Möglichkeiten, um auf " "die TOML Datenstruktur mit TOML Fortran zuzugreifen. Es nimmt eine " "opionierten Ansicht auf, wie die Datenstruktur gelesen und verändert " "werden soll, was für einen Großteil der Anwendungen funktioniert." #: ../tutorial/getting-started.rst:62 msgid "Now we will actually read a TOML document and pass it to our reader." msgstr "Wir lesen jetzt ein echtes TOML Dokument und übergeben es an den Reader." #: ../tutorial/getting-started.rst:64 msgid "input.toml" msgstr "" #: ../tutorial/getting-started.rst:70 msgid "" "We adapt our command line driver to read the file ``input.toml`` and " "output the values as before" msgstr "" "Wir adaptieren unseren Befehlszeilen-Driver, um die Datei ``input.toml`` " "zu lesen und die Werte wie vorher auszugeben." #: ../tutorial/getting-started.rst:72 msgid "app/readin.f90" msgstr "" #: ../tutorial/getting-started.rst:76 msgid "" "Running the program with fpm shows that we were able to read the correct " "values from the document" msgstr "" "Durch das Ausführen des Programms mit fpm kannst Du sehen, dass wir die " "korrekten Werte aus dem Dokument gelesen haben." #: ../tutorial/getting-started.rst:85 msgid "" "You can again use the serializer to write the final data structure, if " "you want to check whether the ``get_value`` interface has added default " "values." msgstr "" "Du kannst das Serializer noch einmal verwenden, um die finale " "Datenstruktur zu schreiben, wenn Du möchtest, ob das ``get_value`` " "interface Standardwerte hinzugefügt hat." #: ../tutorial/getting-started.rst:89 msgid "" "In this tutorial you have learned out to read simple data structures from" " TOML documents. You can now" msgstr "" "In diesem Tutorial hast Du gelernt, einfache Datenstrukturen aus TOML " "Dokumenten zu lesen. Du kannst nun" #: ../tutorial/getting-started.rst:92 msgid "define the logic to read data from TOML structures" msgstr "die Logik zum Lesen von Daten aus TOML Strukturen definieren" #: ../tutorial/getting-started.rst:93 msgid "provide default values in your parser as you define the input structure" msgstr "" "Standardwerte in Deinen Parsern definieren, wenn Du die Eingabestruktur " "definierst" #: ../tutorial/getting-started.rst:94 msgid "read an actual TOML document from a file" msgstr "ein echtes TOML Dokument aus einer Datei lesen" #: ../tutorial/getting-started.rst:95 msgid "write TOML documents from your data structures" msgstr "aus Deinen Datenstrukturen TOML Dokumente schreiben" #: ../tutorial/index.rst:4 msgid "Tutorial courses" msgstr "Tutorial Kurse" #: ../tutorial/index.rst:6 msgid "" "This section contains self-contained courses teaching the usage of TOML " "Fortran. Each tutorial is a full example for a specific application " "covering several aspects of the library." msgstr "" "Dieser Abschnitt enthält eine Reihe von Kursen, die Dich durch die " "Benutzung von TOML Fortran leiten. Jedes Tutorial ist ein vollständiges " "Beispiel für eine bestimmte Anwendung, die mehrere Aspekte der Bibliothek" " abdeckt." #: ../tutorial/index.rst:9 msgid "Contributions are welcome!" msgstr "Beiträge sind willkommen!" #: ../tutorial/index.rst:12 msgid "" "If you have written on TOML Fortran and want to contribute to the " "documentation, feel free to fork the repository and submit a pull " "request." msgstr "" "Wenn Du zu TOML Fortran geschrieben hast und zur Dokumentation beitragen " "willst, kannst Du gerne das Repository forken und einen Pull Request " "öffen." #: ../tutorial/json.rst:2 msgid "Writing a custom lexer" msgstr "" msgid "Difficulty: Intermediate" msgstr "" #: ../tutorial/json.rst:8 msgid "" "Many programs already come with their input formats, switching to a " "different format requires establishing some way to get backward " "compatibility for older inputs. When transitioning to TOML Fortran " "reading of the input will use the provided TOML data structures. If the " "previous input format is sufficiently compatible, it can be parsed into a" " matching TOML data structure and allow to seamlessly use of the TOML " "format going forward while still providing compatibility for previously " "written inputs." msgstr "" #: ../tutorial/json.rst:12 msgid "" "This tutorial is meant to teach how lexing in TOML Fortran works and " "enable the reader to implement their custom lexer for their custom " "format. There is no guarantee that a custom input format can be ported by" " creating a custom lexer, since the format needs to fulfill some basic " "requirements, like providing typed values. For this tutorial, we will " "choose `JSON `_ as our input format and walk through " "all the steps to create a new lexer from scratch." msgstr "" #: ../tutorial/json.rst:18 msgid "" "The choice of JSON for this tutorial is not a coincidence. TOML Fortran " "does implement this lexer to parse JSON files into TOML data structures " "to support the encoding tests in the validation suite of `BurntSushi" "/toml-test `_." msgstr "" #: ../tutorial/json.rst:23 msgid "This tutorial makes partial use of the internal API of TOML Fortran." msgstr "" #: ../tutorial/json.rst:27 msgid "Identifying limitation" msgstr "" #: ../tutorial/json.rst:29 msgid "" "Before we start to implement our custom lexer, we need to identify any " "limitations of the TOML data structures to represent our custom format. " "TOML documents always have a table at the document root, there is no way " "to represent a JSON array or single value in TOML. Furthermore, JSON " "supports the value type ``null``, which is not representable in TOML. We " "have two choices here, either we can flag ``null`` values as an invalid " "token or we can replace them in the lexer with something else like an " "empty table. Finally, there are other details we have to take into " "account, like how JSON is handling duplicate keys, for most of the " "implementation-dependent cases we will follow the rules TOML provides." msgstr "" #: ../tutorial/json.rst:35 msgid "" "This tutorial by no means aims for offering a fully compliant parser as " "we already fail for top-level arrays or ``null`` type values. For a " "custom format, this might be even more challenging, especially if the " "format is defined by only a single implementation." msgstr "" #: ../tutorial/json.rst:40 msgid "" "Writing a compliant JSON parser can quickly become quite challenging (see" " `Parsing JSON is a Minefield " "`_)." msgstr "" #: ../tutorial/json.rst:42 msgid "" "But format limitations can go both ways, there are of course also " "features in TOML we cannot express in JSON. However, since we want to map" " JSON to TOML and not the other way round we do not have to worry about " "limitations present in JSON. Every feature available in TOML " "representable in the previous input format will be an incentive to switch" " to the new format." msgstr "" #: ../tutorial/json.rst:48 msgid "" "For the actual application of the JSON parser in the validation suite, " "this problem is solved by not using only strings to represent values and " "adding type annotations. In TOML Fortran these annotations are mapped " "back by pruning the read JSON data structure. The pruning is done via a " "visitor which is accepted after the data structure has been completely " "parsed." msgstr "" #: ../tutorial/json.rst:54 msgid "Creating the lexer" msgstr "" #: ../tutorial/json.rst:56 msgid "" "First, we start by creating a new subclass of the abstract base class " "(ABC) imported from the *tomlf_de_abc* module." msgstr "" #: ../tutorial/json.rst:58 msgid "src/json_lexer.f90 (json_lexer)" msgstr "" #: ../tutorial/json.rst:63 msgid "" "We start by creating a constructor to consume an input file and turn it " "into a string to advance through." msgstr "" #: ../tutorial/json.rst:65 msgid "src/json_lexer.f90 (new_lexer_from_file)" msgstr "" #: ../tutorial/json.rst:70 msgid "" "Using a formatted unit is more inefficient compared to reading the whole " "file with direct access, but needed in case we are dealing with the " "standard input. We make sure to error out if we get direct access or " "stream access units since we cannot reliably read those." msgstr "" #: ../tutorial/json.rst:73 msgid "src/json_lexer.f90 (new_lexer_from_unit)" msgstr "" #: ../tutorial/json.rst:78 msgid "" "Finally, we sometimes also need to read from a string, there we add a " "constructor which can create a lexer for a string input." msgstr "" #: ../tutorial/json.rst:80 msgid "src/json_lexer.f90 (new_lexer_from_string)" msgstr "" #: ../tutorial/json.rst:85 msgid "" "The parser might need access to some of the internal data of the lexer, " "which is done via the *get_info* procedure." msgstr "" #: ../tutorial/json.rst:87 msgid "src/json_lexer.f90 (get_info)" msgstr "" #: ../tutorial/json.rst:94 msgid "Identifying tokens" msgstr "" #: ../tutorial/json.rst:96 msgid "" "Now that we can instantiate the lexer we need to implement the " "possibility to recognize tokens, this is done with the *next* method. We " "start with creating the actual tokenization step in the *next_token* " "procedure, which we will call in the *next* method." msgstr "" #: ../tutorial/json.rst:99 msgid "src/json_lexer.f90 (next_token)" msgstr "" #: ../tutorial/json.rst:104 msgid "" "As a first action, we will advance the internal state of the lexer by " "consuming the last token. For convenience, we save the position in the " "source string in the *pos* and *prev* variables." msgstr "" #: ../tutorial/json.rst:107 ../tutorial/json.rst:114 ../tutorial/json.rst:126 msgid "src/json_lexer.f90 (next_token, continued)" msgstr "" #: ../tutorial/json.rst:112 msgid "" "The next thing we check is if we have exhausted the input string and if " "so we return the *end of file* token." msgstr "" #: ../tutorial/json.rst:119 msgid "" "Now we can inspect the current character from the source string and " "decide which token it should be labeled. The character set is quite " "simple, we have to consider opening and closing brackets and braces, for " "arrays and tables, respectively, commas, colons, strings, and whitespace." " We will be explicitly producing whitespace tokens here rather than " "skipping it since the parser can gracefully handle whitespace. However, " "we have to consider that newlines have semantical meaning in TOML while " "they are only considered whitespace in JSON and therefore we will only " "produce whitespace tokens." msgstr "" #: ../tutorial/json.rst:124 msgid "We use a select case statement to decide which token to produce." msgstr "" #: ../tutorial/json.rst:131 msgid "" "To wrap up the lexing we will try to identify unknown tokens as well as " "possible trying to advance to the next terminating character. For the " "terminating characters, we choose whitespace as well as control " "characters and place those in the module scope." msgstr "" #: ../tutorial/json.rst:134 msgid "src/json_lexer.f90 (terminated)" msgstr "" #: ../tutorial/json.rst:141 msgid "" "We are cheating a bit here since we declare the colon as an *equal* " "token. This way we can use the same lexer for both JSON and TOML and " "still have the same parsing rules." msgstr "" #: ../tutorial/json.rst:144 msgid "" "One special case to consider is literals, like strings numbers or " "booleans. To not clutter the logic here we create separate routines for " "parsing the respective literal values. For obtaining string values we " "will implement this as *next_string*. Here we cannot simply advance to " "the next quote character, since we need to handle escape characters " "gracefully. While doing so we can also ensure that the escape sequences " "found are valid and not malformed." msgstr "" #: ../tutorial/json.rst:150 msgid "src/json_lexer.f90 (next_string)" msgstr "" #: ../tutorial/json.rst:155 msgid "" "Strings can only contain printable characters, therefore we check for " "valid string characters using a small *valid_string* function for each " "character." msgstr "" #: ../tutorial/json.rst:157 msgid "src/json_lexer.f90 (valid_string)" msgstr "" #: ../tutorial/json.rst:162 msgid "" "We also need to identify numbers, mapping to either integers or floats in" " TOML, which is done via *next_number*." msgstr "" #: ../tutorial/json.rst:164 msgid "src/json_lexer.f90 (next_number)" msgstr "" #: ../tutorial/json.rst:169 msgid "To support boolean values we implement a *next_boolean* procedure." msgstr "" #: ../tutorial/json.rst:171 msgid "src/json_lexer.f90 (next_boolean)" msgstr "" #: ../tutorial/json.rst:176 msgid "" "With this logic available we can now generate all required tokens for " "parsing JSON." msgstr "" #: ../tutorial/json.rst:180 msgid "" "Moving most of the validation logic in the tokenization simplifies the " "actual extraction of the value as we have to deal with fewer edge cases." msgstr "" #: ../tutorial/json.rst:182 msgid "" "Now we can wrap up the *next* procedure, instead of directly returning " "the token we will make some adjustments to the token stream here. In " "general, this is the right place to buffer tokens, perform overflow " "checks, or detect unclosed groups, we will only use it to insert two " "additional tokens to inject a top-level key." msgstr "" #: ../tutorial/json.rst:185 msgid "src/json_lexer.f90 (next)" msgstr "" #: ../tutorial/json.rst:190 msgid "" "This will direct the parser to leave the root document where newlines are" " semantically relevant since we cannot produce such newline tokens in our" " JSON lexer." msgstr "" #: ../tutorial/json.rst:194 msgid "" "If we want to support *null* values, how would we have to modify our " "lexer to produce for example an empty table ``{}`` instead, *i.e.* a " "*lbrace* and *rbrace* token?" msgstr "" #: ../tutorial/json.rst:198 msgid "Extracting values" msgstr "" #: ../tutorial/json.rst:200 msgid "" "Before we can connect our lexer to the existing TOML parser we have to " "implement the extraction of the values. The parser itself will use the " "*extract* member functions to obtain values for the respective tokens and" " never directly access the character stream." msgstr "" #: ../tutorial/json.rst:203 msgid "" "To extract the string value we implement the *extract_string* procedure. " "We will also use the *extract_string* routine to catch the *keypath* " "token we inserted in the token stream and return the wanted dummy value." msgstr "" #: ../tutorial/json.rst:206 msgid "src/json_lexer.f90 (extract_string)" msgstr "" #: ../tutorial/json.rst:211 msgid "" "Similarly, we implement the *extract_integer*, instead of using an " "internal read, we implement the reading ourselves." msgstr "" #: ../tutorial/json.rst:213 msgid "src/json_lexer.f90 (extract_integer)" msgstr "" #: ../tutorial/json.rst:218 msgid "" "For floating point numbers implemented in *extract_float* we will just " "use an internal read." msgstr "" #: ../tutorial/json.rst:220 msgid "src/json_lexer.f90 (extract_float)" msgstr "" #: ../tutorial/json.rst:225 msgid "" "The last token we can produce and extract from our lexer is are boolean " "values, which we implement in *extract_boolean*." msgstr "" #: ../tutorial/json.rst:227 msgid "src/json_lexer.f90 (extract_boolean)" msgstr "" #: ../tutorial/json.rst:232 msgid "" "We create a mocked routine for *extract_datetime* since we cannot produce" " this token in JSON." msgstr "" #: ../tutorial/json.rst:234 msgid "src/json_lexer.f90 (extract_datetime)" msgstr "" #: ../tutorial/json.rst:239 msgid "" "This provides our lexer with full functionality regarding the extraction " "of values needed for parsing and creating data structures." msgstr "" #: ../tutorial/json.rst msgid "full source" msgstr "" #: ../tutorial/json.rst:243 ../tutorial/json.rst:338 msgid "" "For completeness here is again the full source of our lexer " "implementation." msgstr "" #: ../tutorial/json.rst:245 msgid "src/json_lexer.f90" msgstr "" #: ../tutorial/json.rst:252 msgid "Verifying the lexer" msgstr "" #: ../tutorial/json.rst:254 msgid "" "We could start right into connecting our lexer with the parser, but we " "have not yet verified that the tokenization and value extraction work as " "expected. For this purpose, we will create some unit tests using the " "`test-drive `_ framework." msgstr "" #: ../tutorial/json.rst:257 msgid "" "As the entry point for our tester, we will use the standard wrapper for " "launching test suites." msgstr "" #: ../tutorial/json.rst msgid "tester program" msgstr "" #: ../tutorial/json.rst:261 msgid "Taken from the `test-drive`_ README" msgstr "" #: ../tutorial/json.rst:263 msgid "test/main.f90" msgstr "" #: ../tutorial/json.rst:268 msgid "" "Our actual test suite for the lexer will be based on a routine called " "*check_token*, which creates a new lexer from a string and retrieves all " "tokens while comparing them with a reference token stream. We then can " "implement our checks by providing a string and a list of tokens to see " "whether our lexer can identify the expected tokens correctly. For " "visualization, we use the *tomlf_diagnostic* module to label the tokens " "in the actual source string." msgstr "" #: ../tutorial/json.rst:272 msgid "test/test_lexer.f90" msgstr "" #: ../tutorial/json.rst:277 msgid "" "These are only a couple of tests, we have much more cases to consider for" " a robust lexer." msgstr "" #: ../tutorial/json.rst:281 msgid "" "Write at least ten more tests for edge cases in the lexer. Make sure to " "include invalid cases and ensure that even invalid tokens are generated " "correctly." msgstr "" #: ../tutorial/json.rst:286 msgid "Connecting to the parser" msgstr "" #: ../tutorial/json.rst:288 msgid "" "Now that we have verified the tokenization process in our lexer we can " "connect our custom lexer to the default TOML parser." msgstr "" #: ../tutorial/json.rst:290 msgid "" "For this purpose we define convenience interfaces called *json_load* / " "*json_loads* similar to the available *toml_load* / *toml_loads* " "interfaces. Other than the TOML-related load interfaces, we will also use" " them to implement necessary post-processing steps for the data " "structure." msgstr "" #: ../tutorial/json.rst:293 ../tutorial/json.rst:342 msgid "src/json_parser.f90" msgstr "" #: ../tutorial/json.rst:298 msgid "" "The *json_load* interface is implemented by *json_load_file* and " "*json_load_unit*. The former is a wrapper that is using the " "*new_lexer_from_file* constructor." msgstr "" #: ../tutorial/json.rst:301 msgid "src/json_parser.f90 (json_load_file)" msgstr "" #: ../tutorial/json.rst:306 msgid "The latter wraps the *new_lexer_from_unit* constructor." msgstr "" #: ../tutorial/json.rst:308 ../tutorial/json.rst:315 msgid "src/json_parser.f90 (json_load_unit)" msgstr "" #: ../tutorial/json.rst:313 msgid "" "Finally, we also provide *json_loads* by implementing *json_load_string* " "using our *new_lexer_from_string* constructor." msgstr "" #: ../tutorial/json.rst:320 msgid "" "These wrappers so far are very straightforward, first setting up a lexer " "instance and invoking the *parse* procedure which will construct the " "actual parser instance and process the token stream. After a successful " "run, the *table* instance will be allocated, for the post-processing, we " "invoke the *prune* routine." msgstr "" #: ../tutorial/json.rst:323 msgid "src/json_parser.f90 (prune)" msgstr "" #: ../tutorial/json.rst:328 msgid "" "Where we effectively retrieve the first child from the root table and " "create a deep copy of it which is then returned." msgstr "" #: ../tutorial/json.rst:332 msgid "" "An alternative approach would be to *pop* the value from the root table " "and return the polymorphic *toml_value* instance. This would have the " "advantage that we can support arrays and values at the root level with " "our JSON loader. The user than has to dispatch the value using a *select " "type* construct or by creating a view using the *cast_to_table* / " "*cast_to_array* / *cast_to_keyval* functions." msgstr "" #: ../tutorial/json.rst:340 msgid "" "Note that this implementation also contains an implementation of a " "*toml_visitor* to prune type annotations used in the validation test " "suite to represent TOML values." msgstr "" #: ../tutorial/json.rst:351 msgid "" "Now we have a working lexer that can tokenize JSON documents into TOML " "parsable tokens. The lexer implemented in TOML Fortran works on a similar" " construction, with the difference that the TOML grammar is much more " "complicated to parse than JSON." msgstr "" #: ../tutorial/json.rst:356 msgid "" "In this tutorial, you have learned about the tokenization process used in" " TOML Fortran. You can now" msgstr "" "In diesem Tutorial hast Du gelernt, einfache Datenstrukturen aus TOML " "Dokumenten zu lesen. Du kannst nun" #: ../tutorial/json.rst:359 msgid "implement a custom lexer based on the TOML tokens" msgstr "" #: ../tutorial/json.rst:360 msgid "verify your lexer against an expected token stream" msgstr "" #: ../tutorial/json.rst:361 msgid "adjust the token stream to direct the parsing process" msgstr "" #: ../tutorial/json.rst:362 msgid "add a post-processing step to prune the resulting data structure" msgstr "" fortran-toml-0.5.0/doc/locales/de/LC_MESSAGES/index.po0000664000175000017500000000752215201541453022345 0ustar alastairalastair# Copyright (C) 2019-2022, Sebastian Ehlert # This file is distributed under the same license as the toml-f package. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: toml-f 0.2.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-30 16:12+0200\n" "PO-Revision-Date: 2022-07-30 16:12+0200\n" "Last-Translator: Sebastian Ehlert\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.10.3\n" #: ../index.rst:113 msgid "Tutorials" msgstr "Tutorials" #: ../index.rst:113 msgid "How-Tos" msgstr "How-Tos" #: ../index.rst:113 msgid "References" msgstr "Referenzen" #: ../index.rst:4 msgid "TOML Fortran library" msgstr "TOML Fortran Bibliothek" msgid "TOML Fortran" msgstr "TOML Fortran" msgid "License" msgstr "Lizenz" msgid "Version" msgstr "Version" msgid "Continuous Integration" msgstr "Continuous Integration" msgid "API docs" msgstr "API Dokumentation" msgid "Documentation Status" msgstr "Dokumentationsstatus" msgid "Coverage" msgstr "Coverage" #: ../index.rst:35 msgid "" "This project provides a library to work with `TOML `_ " "files and data structures and allows to define data serialization and " "deserialization in Fortran. The currently supported TOML standard is " "`version 1.0.0 `__. The TOML Fortran project " "is hosted at GitHub at `toml-f/toml-f " "`__." msgstr "" "Dieses Projekt stellt eine Bibliothek um mit `TOML `_ " "Dokumenten zu arbeiten, Datenstrukturen zu definieren und diese mit " "Fortran zu serialisieren und zu deserialisieren. Derzeit wird der `TOML " "Standard in Version 1.0.0 `__ unterstützt. Das" " TOML Fortran Projekt ist auf GitHub unter `toml-f/toml-f " "`__ gehostet." #: ../index.rst:49 msgid ":octicon:`mortar-board` Tutorials" msgstr ":octicon:`mortar-board` Tutorials" #: ../index.rst:51 msgid "" "Guides and courses for using TOML with complete and self-contained " "examples." msgstr "" "Anleitungen und Kurse zur Verwendung von TOML mit kompletten und " "eigenständigen Beispielen." #: ../index.rst:60 msgid ":octicon:`book` Recipes" msgstr ":octicon:`book` Rezepte" #: ../index.rst:62 msgid "Examples and recipes for solving common tasks with TOML Fortran." msgstr "Beispiele und Rezepte für die häufigsten Aufgaben mit TOML Fortran." #: ../index.rst:70 msgid ":octicon:`gear` Reference" msgstr ":octicon:`gear` Referenz" #: ../index.rst:72 msgid "Generated documentation of all procedures and derived types available." msgstr "Dokumentation aller Prozeduren und abgeleiteten Typen." #: ../index.rst:81 msgid ":octicon:`arrow-down` Installation" msgstr ":octicon:`arrow-down` Installation" #: ../index.rst:83 msgid "Instructions for installing, updating or compiling TOML Fortran." msgstr "Anleitungen zur Installation, Aktualisierung oder Kompilierung von " "TOML Fortran." #: ../index.rst:91 msgid ":octicon:`mark-github` Repository" msgstr ":octicon:`mark-github` Repository" #: ../index.rst:93 msgid "GitHub repository for the development of TOML Fortran." msgstr "GitHub Repository für die Entwicklung von TOML Fortran." #: ../index.rst:102 msgid ":octicon:`link` External" msgstr ":octicon:`link` Extern" #: ../index.rst:104 msgid "External links to other resources, blogs, etc." msgstr "Externe Links zu anderen Ressourcen, Blogs, etc." #: ../index.rst:106 msgid "" "TOML Fortran is used for example in the Fortran package manager (`fpm " "`_), a typical package manifest specified " "in TOML is shown below" msgstr "" "TOML Fortran wird zum Beispiel in dem Fortran Paketmanager (`fpm " "`_) verwendet, ein typisches " "Paketmanifest in TOML wird unten angezeigt" #: ../index.rst:108 msgid "fpm.toml" msgstr "" fortran-toml-0.5.0/doc/locales/de/LC_MESSAGES/how-to.po0000664000175000017500000021073515201541453022455 0ustar alastairalastair# Copyright (C) 2019-2022, Sebastian Ehlert # This file is distributed under the same license as the toml-f package. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: toml-f 0.2.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-31 17:47+0200\n" "PO-Revision-Date: 2022-07-31 14:30+0200\n" "Last-Translator: Sebastian Ehlert\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.10.3\n" #: ../how-to/array.rst:2 msgid "Working with arrays" msgstr "" #: ../how-to/array.rst:4 msgid "" "TOML supports *array* data types: flat, ordered containers holding " "multiple values. Arrays are dynamically sized (*i.e.* do not need specify" " the number of elements before using an array) and can contain elements " "of any type supported by TOML." msgstr "" #: ../how-to/array.rst:7 msgid "" "The following program parses and stores an array of integers then prints " "the values to stdout:" msgstr "" #: ../how-to/array.rst:9 msgid "app/parse_array.f90" msgstr "" #: ../how-to/array.rst:14 msgid "" "The simplest way to parse an array-valued key in TOML Fortran is to use " "the ``get_value`` interface, which has an overload to handle arrays. " "First, we need to use a temporary variable ``arr`` (of type " "``toml_array``), as the array's elements can be of potentially any type. " "``arr`` is automatically associated and allocated by ``get_value``, which" " we can use to determine how much space is needed to store the array's " "contents. Finally, we must iterate over all elements of the temporary " "``toml_array`` and assign them to an element in our final data structure " "``arr_data`` (which is a standard Fortran array)." msgstr "" #: ../how-to/array.rst:19 msgid "" "As a test, let's run the above program using the following TOML table " "(``array.toml``):" msgstr "" #: ../how-to/array.rst:21 msgid "array.toml" msgstr "" #: ../how-to/array.rst:26 msgid "This produces the following output:" msgstr "" #: ../how-to/array.rst:32 msgid "" "As mentioned above, TOML arrays can contain elements of any type " "supported by TOML. The TOML standard even supports heterogeneous arrays " "containing elements of multiple different types. TOML Fortran supports " "this through the generic ``toml_array`` type." msgstr "" #: ../how-to/array.rst:36 msgid "" "Elements of ``toml_array`` may be of any value but require extra " "processing before they can be used in most Fortran programs, since " "Fortran arrays must only contain elements of a single type known at " "compile time. This is achieved through the generic ``get_value`` " "interface, which automatically coerces elements of the ``toml_array`` to " "the correct type based on the variable it is copied to." msgstr "" #: ../how-to/array.rst:41 msgid "" "The value in ``get_value`` is an ``intent(out)`` argument, in case the " "input and output parameter are incompatible, it is not initialized and " "the actual value is dependent on the compiler settings. By passing an " "integer value to the optional *stat* argument, the procedure will return " "a non-zero value to indicate an error." msgstr "" #: ../how-to/array.rst:46 msgid "Accessing nested arrays" msgstr "" #: ../how-to/array.rst:48 msgid "" "TOML arrays can be nested to produce an *array-of-arrays*. Both the top-" "level and nested child arrays are potentially variable-length and can " "contain any TOML data-type (including further nested arrays)." msgstr "" #: ../how-to/array.rst:50 msgid "" "The following program reads a TOML file with an array-of-arrays nested a " "single level deep, and prints its values to stdout:" msgstr "" #: ../how-to/array.rst:52 msgid "app/nested_array.f90" msgstr "" #: ../how-to/array.rst:56 msgid "" "First, we need to call ``get_value`` to get a pointer to the top-level " "array then iterate through its elements, calling ``get_value`` on each to" " get a pointer to the child array. Finally, we iterate through the " "individual sub-arrays and process their elements one-by-one." msgstr "" #: ../how-to/array.rst:59 msgid "" "This approach has the advantage of being able to deal with nested arrays " "of arbitrary length, such as the TOML table below:" msgstr "" #: ../how-to/array.rst:65 msgid "Which produces the following output:" msgstr "" #: ../how-to/array.rst:78 msgid "" "A TOML array-of-arrays does not necessarily map cleanly to a regular " "Fortran multidimensional array. Multidimensional Fortran arrays must be " "rectangular (*i.e.* rows must contain the same number of elements), so an" " array-of-arrays with variable sized nested arrays cannot be directly " "mapped to a primitive Fortran type and must be represented using a " "compound data type." msgstr "" #: ../how-to/datetime.rst:2 msgid "Date time compatibility" msgstr "Kompatibilität mit Datum- und Zeitangaben" #: ../how-to/datetime.rst:4 msgid "" "TOML values can represent date times and are also accessible via the " "build interfaces *get_value* and *set_value*. However, the internal " "representation of a date time value might not be compatible with other " "libraries dealing with date times. This recipes show how the build " "interface can be extended to also support common date time libraries." msgstr "" "TOML Werte können Datum- und Zeitangaben representieren und sind auch " "über die Build-Interfaces *get_value* und *set_value* zugänglich. Die " "interne Repräsentation eines Datum- und Zeitwertes kann zu anderen " "Bibliotheken inkompatibel sein kann. Dieses Rezept zeigt wie das Build-" "Interface erweitert werden kann, um andere Bibliotheken zu unterstützen." #: ../how-to/datetime.rst:9 msgid "" "`datetime-fortran `_" msgstr "" #: ../how-to/datetime.rst:11 msgid "" "The *datetime-fortran* library provides an opaque object representing a " "date time value and several features for manipulating it. An fpm project " "using both TOML Fortran and *datetime-fortran* can be setup using the " "following dependencies section in the manifest." msgstr "" "Die *datetime-fortran*-Bibliothek bietet ein opaques Objekt, das eine " "Datum- und Zeitangabe repräsentiert und verschiedene Funktionen zur " "Verarbeitung und Manipulation bereitstellt. Ein fpm-Projekt, das TOML-" "Fortran und *datetime-fortran* verwendet, kann mit dem folgenden " "Dependencies-Abschnitt im Manifest eingerichtet werden." #: ../how-to/datetime.rst:20 ../how-to/datetime.rst:39 msgid "" "To define a compatibility between the two derived types representing a " "date time value we extend the *get_value* and *set_value* generic " "interfaces." msgstr "" "Um eine Kompatibilität zwischen den beiden abgeleiteten Typen zu " "definieren, erweitern wir die *get_value* und *set_value* generische " "Interfaces." #: ../how-to/datetime.rst:22 ../how-to/datetime.rst:41 msgid "src/compat.f90" msgstr "" #: ../how-to/datetime.rst:28 msgid "`M_time `_" msgstr "" #: ../how-to/datetime.rst:30 msgid "" "The *M_time* library provides a transparent object representing a date " "time value, with a rich functional and object oriented API for inspecting" " and manipulating it. An fpm project using both TOML Fortran and *M_time*" " can be setup using the following dependencies section in the manifest." msgstr "" "Die *M_time*-Bibliothek bietet ein transparentes Objekt, das eine Datum- " "und Zeitangabe repräsentiert, mit einem reichhaltigen Funktions- und " "Objektorientierten API zur Inspektion und Manipulation. Ein fpm-Projekt, " "das TOML-Fortran und *M_time* verwendet, kann mit dem folgenden " "Dependencies-Abschnitt im Manifest eingerichtet werden." #: ../how-to/error.rst:2 msgid "Reporting errors" msgstr "" #: ../how-to/error.rst:4 msgid "" "TOML data structures can record their origin in the original TOML " "document, which can be used to report errors with rich context " "information. The recipes here describe how to obtain the context for " "producing error messages and diagnostics using the origin information of " "the data structures." msgstr "" #: ../how-to/error.rst:9 msgid "Loading with rich context" msgstr "" #: ../how-to/error.rst:11 msgid "" "To make use of the origin information, the context from loading the " "document has to be preserved. This can be archived by passing the " "optional ``context`` argument to the loading interface to request the " "document context to be exported. To obtain the context object we have to " "request it when reading the TOML document." msgstr "" #: ../how-to/error.rst:15 ../how-to/error.rst:36 ../how-to/error.rst:127 #: ../how-to/error.rst:157 ../how-to/error.rst:240 msgid "app/main.f90" msgstr "" #: ../how-to/error.rst:20 msgid "We define a simple data type for a configuration for this recipe." msgstr "" #: ../how-to/error.rst:22 ../how-to/error.rst:29 ../how-to/error.rst:59 #: ../how-to/error.rst:90 msgid "src/config.f90" msgstr "" #: ../how-to/error.rst:27 msgid "" "To report errors, we now not only use the TOML data structure, but also a" " context object, which allows us to create the report." msgstr "" #: ../how-to/error.rst:34 msgid "" "To provide the data structure we create a simple driver to read a TOML " "document." msgstr "" #: ../how-to/error.rst:40 msgid "" "Now we can try with an incorrect configuration file, where we pass a " "string instead of a float to our option." msgstr "" #: ../how-to/error.rst:42 ../how-to/error.rst:66 ../how-to/error.rst:102 msgid "config.toml" msgstr "" #: ../how-to/error.rst:46 msgid "" "The error message is produced and shows the exact origin of the value in " "the document." msgstr "" #: ../how-to/error.rst:57 msgid "" "Now we also have to handle the case where the value can be read " "correctly, but is incorrect for our application, like a negative " "timestep." msgstr "" #: ../how-to/error.rst:64 msgid "" "The origin information will still be available and allow us to make a " "rich report about the error in the input." msgstr "" #: ../how-to/error.rst:70 msgid "The resulting error message is shown below." msgstr "" #: ../how-to/error.rst:83 msgid "" "Each TOML data structure has an *origin* attribute, which can be used " "together with the report function of the context. In case the origin " "cannot be mapped to a single token, *e.g.* for the root table, the value " "of the origin will be zero. The report function will only produce labels " "for non-zero origins and gracefully ignore data without origin in the " "current context." msgstr "" #: ../how-to/error.rst:87 msgid "" "The reporting function is not limited to errors, it can also produce " "warnings or informational messages. For this purpose, we select the " "appropriate ``toml_level`` for the report." msgstr "" #: ../how-to/error.rst:97 msgid "" "The ``toml_level`` parameter provides a statically initialized derived " "type enumerating all available report levels. You can think of it as an " "enumerator with a proper namespace." msgstr "" #: ../how-to/error.rst:100 msgid "We can test this for the following example." msgstr "" #: ../how-to/error.rst:106 msgid "The resulting warning is shown below." msgstr "" #: ../how-to/error.rst msgid "full source" msgstr "" #: ../how-to/error.rst:119 msgid "The full *demo_config* module is given here." msgstr "" #: ../how-to/error.rst:121 ../how-to/error.rst:142 ../how-to/error.rst:150 #: ../how-to/error.rst:185 ../how-to/error.rst:234 msgid "src/dependency.f90" msgstr "" #: ../how-to/error.rst:125 msgid "The driver for running the examples is given below." msgstr "" #: ../how-to/error.rst:133 msgid "Multiline reports" msgstr "" #: ../how-to/error.rst:135 msgid "" "In some cases, multiple labels are required to express the context of the" " report correctly. This feature is available with the context object, by " "providing the origin of the two data structures in the reporting " "function." msgstr "" #: ../how-to/error.rst:138 msgid "" "An example of this is the dependency table in fpm, where we can either " "provide a local dependency using the *path* key or a remote dependency " "using the *git* key, but not both at the same time." msgstr "" #: ../how-to/error.rst:140 msgid "" "We declare a simple dummy dependency storing only the dependency name for" " demonstration purposes." msgstr "" #: ../how-to/error.rst:147 msgid "" "We iterate over the list of all subtables in the dependency table and " "read the actual dependency. In case an entry is not a subtable we will " "raise an error, since a package manifest can contain multiple dependency " "tables, we will report which table we are currently in as additional " "context." msgstr "" #: ../how-to/error.rst:155 msgid "" "To provide the *dependencies* table we create a simple driver to read a " "TOML document." msgstr "" #: ../how-to/error.rst:161 msgid "An example triggering the error is shown below." msgstr "" #: ../how-to/error.rst:163 ../how-to/error.rst:194 ../how-to/error.rst:213 #: ../how-to/integration.rst:15 msgid "fpm.toml" msgstr "" #: ../how-to/error.rst:167 msgid "" "Running this example will produce the following error showing lines 1 and" " 3 of our example input." msgstr "" #: ../how-to/error.rst:181 msgid "" "Now we want to implement the actual conflicting case described above. " "Here we just read the two strings from the *git* and *path* entry. Note " "that the *get_value* interface will not allocate the string if no value " "is present, which allows to conveniently check for success via allocation" " status of the strings." msgstr "" #: ../how-to/error.rst:190 msgid "" "To preserve the order from the input we can compare the *origin* values " "of the two retrieved strings and produce the appropriate error message." msgstr "" #: ../how-to/error.rst:192 msgid "" "In this example, the *git* entry was defined first and a conflicting " "*path* entry is provided afterward." msgstr "" #: ../how-to/error.rst:198 msgid "The order is reported correctly in the produced error message shown below." msgstr "" #: ../how-to/error.rst:211 msgid "The other way round is also possible as shown in this example." msgstr "" #: ../how-to/error.rst:217 msgid "" "The error message is adjusted accordingly and now reports a conflicting " "*git* entry to the already defined *path* entry." msgstr "" #: ../how-to/error.rst:232 msgid "The full *demo_dependency* module is given provided below." msgstr "" #: ../how-to/error.rst:238 msgid "The driver for the examples is given here." msgstr "" #: ../how-to/error.rst:246 msgid "Color support" msgstr "" #: ../how-to/error.rst:248 msgid "" "All reports also support colorful terminal output. For this purpose, we " "can use the provided *toml_terminal* which can be instantiated with color" " support." msgstr "" #: ../how-to/error.rst:259 msgid "" "To activate the color support for error messages produced in the load " "interface the optional argument *config* takes a *toml_parser_config* " "instance." msgstr "" #: ../how-to/error.rst:265 msgid "" "Alternatively, an instance of a *toml_terminal* can be passed to the " "*toml_parser_config* constructor." msgstr "" #: ../how-to/error.rst:267 msgid "" "For working with the *context* instance returned by the load interface we" " need a terminal to activate the colorful output passed to the optional " "*color* argument." msgstr "" #: ../how-to/error.rst:274 msgid "The *terminal* can also be used to colorize regular text output." msgstr "" #: ../how-to/error.rst:285 msgid "" "If the terminal is not initialized or the color support is explicitly " "disabled by passing ``.false.`` to the constructor, the output will be " "plain text." msgstr "" #: ../how-to/index.rst:4 msgid "How-to guides" msgstr "" #: ../how-to/index.rst:6 msgid "" "This section contains practical guides for working with TOML Fortran. " "Each recipe is tailored for a specific problem and use case it is trying " "to solve." msgstr "" "Dieser Abschnitt enthält praktische Anleitungen für die Arbeit mit TOML " "Fortran. Jede Rezepte ist für eine bestimmte Problem- und Nutzungs-" "Szenarien angepasst." #: ../how-to/index.rst:9 msgid "Contributions are welcome!" msgstr "Beiträge sind willkommen!" #: ../how-to/index.rst:12 msgid "" "If you have written on TOML Fortran and want to contribute to the " "documentation, feel free to fork the repository and submit a pull " "request." msgstr "" "Wenn Du über TOML Fortran geschrieben hast und zur Dokumentation " "beigetreten möchtest, dann kannst Du das Repository forken und eine Pull " "Request öffnen." #: ../how-to/installation.rst:4 msgid "Installing TOML Fortran" msgstr "Installieren von TOML Fortran" #: ../how-to/installation.rst:6 msgid "" "This guide will walk you through installing the latest version of TOML " "Fortran. If you know your way around fpm, CMake or meson, checkout the " ":ref:`integration guide ` to allow on-demand compilation of " "TOML Fortran as well as discovery of installed libraries." msgstr "" "Diese Anleitung führt Dich durch die Installation der neuesten Version " "von TOML Fortran. Falls Du FPM, CMake oder meson verwendest, schaue Dir " "die :ref:`Integrationsanleitung ` an, um TOML Fortran " "automatisch in mitzukompilieren oder bereits installierte Bibliotheken zu" " finden." #: ../how-to/installation.rst:11 msgid ":fab:`apple` :fab:`linux` :fab:`windows` Installing from conda-forge" msgstr ":fab:`apple` :fab:`linux` :fab:`windows` Installation von conda-forge" msgid "Conda" msgstr "Conda" #: ../how-to/installation.rst:22 msgid "" "This project is packaged for the *mamba* package manager and available on" " the *conda-forge* channel. To install the *mamba* package manager we " "recommend the `mambaforge `_ installer. If the *conda-forge* channel is " "not yet enabled, add it to your channels with" msgstr "" "Dieses Projekt ist für den *mamba*-Paketmanager verfügbar und wird auf " "dem *conda-forge*-Kanal zur Verfügung gestellt. Um den " "*mamba*-Paketmanager zu installieren, empfehlen wir den `mambaforge " "`_-Installer. Falls " "der *conda-forge*-Kanal noch nicht aktiviert ist, füge ihn zu Deinen " "Kanälen hinzu mit" #: ../how-to/installation.rst:31 msgid "" "Once the *conda-forge* channel has been enabled, TOML Fortran can be " "installed with *mamba*:" msgstr "" "Wenn der *conda-forge*-Kanal aktiviert ist, kann TOML Fortran mit *mamba*" " installiert werden:" #: ../how-to/installation.rst:37 msgid "" "It is possible to list all of the versions of TOML Fortran available on " "your platform with *mamba*:" msgstr "" "Es ist möglich, alle verfügbaren Versionen von TOML Fortran für Dein " "System mit *mamba* aufzulisten:" #: ../how-to/installation.rst:45 msgid ":fab:`freebsd` FreeBSD ports" msgstr ":fab:`freebsd` FreeBSD Ports" msgid "FreeBSD" msgstr "FreeBSD" #: ../how-to/installation.rst:51 msgid "A port for FreeBSD is available" msgstr "Ein FreeBSD-Port ist verfügbar" #: ../how-to/installation.rst:57 msgid "In case no package is available build the port using" msgstr "Falls kein Paket verfügbar ist, kann der Port folgendermaßen gebaut werden" #: ../how-to/installation.rst:64 msgid "" "For more information see the `toml-f port details " "`_." msgstr "" "Für weitere Informationen schau Dir die `toml-f Port-Details " "`_ an." #: ../how-to/installation.rst:68 msgid ":fab:`apple` :fab:`linux` Building with spack" msgstr ":fab:`apple` :fab:`linux` Mit spack bauen" msgid "Spack" msgstr "" #: ../how-to/installation.rst:74 msgid "" "This project is available with the `spack `_ package " "manager. You can install the TOML Fortran package with" msgstr "" "Dieses Projekt ist mit dem `spack `_-Paketmanager " "verfügbar. Du kannst TOML Fortran mit dem folgenden Befehl installieren:" #: ../how-to/installation.rst:82 msgid "" "To depend on TOML Fortran in your `spack`_ package you can add a " "dependency with" msgstr "" "Um auf TOML Fortran in deinem `spack`_-Paket zu verwenden, kannst du eine" " Abhängigkeit hinzufügen mit" #: ../how-to/installation.rst:90 msgid ":fab:`linux` Using EasyBuild" msgstr ":fab:`linux` Mit EasyBuild" #: ../how-to/installation.rst:92 msgid "" "TOML Fortran is available with `EasyBuild " "`_. You can check the " "available configurations using the search option" msgstr "" "TOML Fortran ist mit `EasyBuild " "`_ verfügbar. Du kannst " "die verfügbaren Konfigurationen mit dem Suchoption auswählen" #: ../how-to/installation.rst:102 msgid "Select one matching configuration and build TOML Fortran using" msgstr "Wähle eine passende Konfiguration aus und bau TOML Fortran mit" #: ../how-to/installation.rst:116 msgid "" "TOML Fortran should now be available via an environment module. For more " "details checkout the `EasyBuild`_ documentation." msgstr "" "TOML Fortran ist nun über ein Umgebungsmodul verfügbar. Für weitere " "Informationen schau Dir die `EasyBuild`_-Dokumentation an." #: ../how-to/installation.rst:121 msgid ":fab:`apple` Using Homebrew" msgstr ":fab:`apple` Mit Homebrew" #: ../how-to/installation.rst:123 msgid "" "TOML Fortran is available in a custom tap at `grimme-lab/homebrew-qc " "`__ for the `brew " "`_ package manager. You can include the tap by using" msgstr "" "TOML Fortran ist in einem eigenen Tap verfügbar unter `grimme-lab" "/homebrew-qc `__ für den `brew" " `_ Paketmanager. Du kannst den Tap mit dem " "folgenden Befehl hinzufügen:" #: ../how-to/installation.rst:131 msgid "" "To build with a custom Fortran compiler you can set the ``FC`` " "environment variable and force to ignore preexisting binary distributions" " from the tap" msgstr "" "Um mit einem eigenen Fortran-Compiler zu bauen, kannst du die " "``FC``-Umgebungsvariable setzen und die vorhandenen Binärdistribution vom" " Tap ignorieren" #: ../how-to/installation.rst:140 msgid "" "TOML Fortran has not yet been submitted to `homebrew-core " "`_." msgstr "" "TOML Fortran wurde noch nicht an `homebrew-core " "https://github.com/homebrew/homebrew-core>`_ eingereicht." #: ../how-to/installation.rst:144 msgid "Building from source" msgstr "Vom Quellcode bauen" #: ../how-to/installation.rst:146 msgid "" "To build this project from the source code in this repository you need to" " have" msgstr "" "Um dieses Projekt aus dem Quellcode in diesem Repository zu bauen, " "benötigst Du" #: ../how-to/installation.rst:148 msgid "a Fortran compiler supporting Fortran 2008" msgstr "einen Fortran-Compiler, der Fortran 2008 unterstützt" #: ../how-to/installation.rst:150 msgid "GFortran 5 or newer" msgstr "GFortran 5 oder neuer" #: ../how-to/installation.rst:151 msgid "Intel Fortran 18 or newer" msgstr "Intel Fortran 18 oder neuer" #: ../how-to/installation.rst:152 msgid "NAG 7 or newer" msgstr "NAG 7 oder neuer" #: ../how-to/installation.rst:154 msgid "One of the supported build systems" msgstr "Eines der unterstützten Buildsysteme" #: ../how-to/installation.rst:156 msgid "`meson `_ version 0.55 or newer" msgstr "`meson `_ Version 0.55 oder neuer" #: ../how-to/installation.rst:157 msgid "`CMake `_ version 3.9 or newer" msgstr "`CMake `_ Version 3.9 oder neuer" #: ../how-to/installation.rst:159 msgid "First, get the source by cloning the repository" msgstr "Erst, hol Dir den Quellcode per Clone des Repositories" #: ../how-to/installation.rst:168 msgid "Using Meson" msgstr "Mit Meson" #: ../how-to/installation.rst:170 msgid "" "To build this project with meson a build-system backend is required, " "*i.e.* `ninja `_ version 1.7 or newer. Setup a " "build with" msgstr "" "Um dieses Projekt mit meson zu bauen, benötigst Du ein Build-System, wie " "`ninja `_ Version 1.7 oder neuer. " #: ../how-to/installation.rst:177 msgid "" "You can select the Fortran compiler by the ``FC`` environment variable. " "To compile the project run" msgstr "" "Du kannst den Fortran-Compiler durch die Umgebungsvariable ``FC`` " "auswählen. Um das Projekt zu kompilieren, rufe den folgenden Befehl auf" #: ../how-to/installation.rst:184 msgid "" "We employ a `validator suite `_ " "to test the standard compliance of this implementation. To use this " "testing a *go* installation is required. The installation of the " "validator suite will be handled by meson automatically without installing" " into the users *go* workspace. Run the tests with" msgstr "" "Wir arbeiten mit einer `Validator-Suite `_ um die Standardkonformität dieser Implementierung zu " "testen. Um diese Test zu durchführen, benötigst Du eine " "*go*-Installation. Die Installation der Validator-Suite wird automatisch " "von meson in den *go*-Arbeitsbereich eingebunden. Die Tests werden " "folgendermaßen aufgerufen" #: ../how-to/installation.rst:193 msgid "" "To run the full decoder test add the benchmark argument. This test will " "currently fail, due to the implementation not yet supporting Unicode " "escape sequences." msgstr "" "Um den kompletten Decoder-Test auszuführen, füge den Benchmark-Parameter " "hinzu. Dieser Test wird zur Zeit fehlschlagen, da die Implementierung " "noch Unicode-Escape-Sequenzen nicht unterstützt." #: ../how-to/installation.rst:200 msgid "" "The binary used for transcribing the TOML documents to the testing format" " is ``_build/test/toml2json`` and can be used to check on per test basis." " Finally, you can install TOML Fortran with" msgstr "" "Das Programm zum Transkribieren von TOML-Dokumenten in das Testformat ist" " ``_build/test/toml2json`` und kann zur Prüfung auf Basis der Tests " "verwendet werden. Zum Abschluss kann TOML Fortran wie folgt installiert " "werden" #: ../how-to/installation.rst:209 msgid "Using CMake" msgstr "Mit CMake" #: ../how-to/installation.rst:211 msgid "" "While meson is the preferred way to build this project it also offers " "CMake support. Configure the CMake build with" msgstr "" "Während meson die bevorzugte Bauweise für dieses Projekt ist, bietet es " "auch CMake-Unterstützung. Konfiguriere den CMake-Build mit" #: ../how-to/installation.rst:218 msgid "" "Similar to meson the compiler can be selected with the ``FC`` environment" " variable. You can build the project using" msgstr "" "Ähnlich wie meson kann der Compiler durch die Umgebungsvariable ``FC`` " "ausgewählt werden. Du kannst das Projekt wie folgt bauen" #: ../how-to/installation.rst:225 msgid "" "To include *toml-f* in your CMake project, check the [example integration" " with CMake](https://github.com/toml-f/tf-cmake-example). The validation " "suite is currently not supported as unit test for CMake builds and " "requires a manual setup instead using the *toml2json* binary. Finally, " "you can install TOML Fortran with" msgstr "" #: ../how-to/installation.rst:235 msgid "Supported compilers" msgstr "Unterstützte Compiler" #: ../how-to/installation.rst:237 msgid "" "This is a non-comprehensive list of tested compilers for TOML Fortran. " "Compilers with the label *latest* are tested with continuous integration " "for each commit." msgstr "" "Dies ist eine nicht-komplettierte Liste von getesteten Compilern für TOML" " Fortran. Compiler mit dem Label *latest* werden getestet mit den " "kontinuierlichen Integrationen für jeden Commit." #: ../how-to/installation.rst:241 ../how-to/installation.rst:260 msgid "Compiler" msgstr "Compiler" #: ../how-to/installation.rst:241 ../how-to/installation.rst:260 msgid "Version" msgstr "Version" #: ../how-to/installation.rst:241 ../how-to/installation.rst:260 msgid "Platform" msgstr "Plattform" #: ../how-to/installation.rst:241 ../how-to/installation.rst:260 msgid "Architecture" msgstr "Architektur" #: ../how-to/installation.rst:241 msgid "version" msgstr "Version" #: ../how-to/installation.rst:243 ../how-to/installation.rst:244 #: ../how-to/installation.rst:245 ../how-to/installation.rst:246 #: ../how-to/installation.rst:247 msgid "GCC" msgstr "" #: ../how-to/installation.rst:243 msgid "11.1, 10.3, 9.4, 8.5, 7.5" msgstr "" #: ../how-to/installation.rst:243 ../how-to/installation.rst:250 #: ../how-to/installation.rst:262 msgid "Ubuntu 20.04" msgstr "" #: ../how-to/installation.rst:243 ../how-to/installation.rst:244 #: ../how-to/installation.rst:248 ../how-to/installation.rst:249 #: ../how-to/installation.rst:250 ../how-to/installation.rst:251 #: ../how-to/installation.rst:252 ../how-to/installation.rst:262 #: ../how-to/installation.rst:263 msgid "x86_64" msgstr "" #: ../how-to/installation.rst:243 ../how-to/installation.rst:244 #: ../how-to/installation.rst:248 ../how-to/installation.rst:250 msgid "0.2.3, latest" msgstr "" #: ../how-to/installation.rst:244 msgid "9.4, 6.5" msgstr "" #: ../how-to/installation.rst:244 msgid "MacOS 10.15.7" msgstr "" #: ../how-to/installation.rst:245 msgid "11.0" msgstr "" #: ../how-to/installation.rst:245 msgid "MacOS 11.0" msgstr "" #: ../how-to/installation.rst:245 msgid "arm64" msgstr "" #: ../how-to/installation.rst:245 ../how-to/installation.rst:246 #: ../how-to/installation.rst:247 ../how-to/installation.rst:249 #: ../how-to/installation.rst:251 ../how-to/installation.rst:252 msgid "0.2.3" msgstr "" #: ../how-to/installation.rst:246 ../how-to/installation.rst:247 msgid "9.4" msgstr "" #: ../how-to/installation.rst:246 ../how-to/installation.rst:247 msgid "CentOS 7" msgstr "" #: ../how-to/installation.rst:246 msgid "ppc64le" msgstr "" #: ../how-to/installation.rst:247 msgid "aarch64" msgstr "" #: ../how-to/installation.rst:248 ../how-to/installation.rst:249 msgid "GCC/MinGW" msgstr "" #: ../how-to/installation.rst:248 msgid "8.1" msgstr "" #: ../how-to/installation.rst:248 ../how-to/installation.rst:249 msgid "Window Server 2019" msgstr "" #: ../how-to/installation.rst:249 msgid "5.3" msgstr "" #: ../how-to/installation.rst:250 ../how-to/installation.rst:251 msgid "Intel" msgstr "" #: ../how-to/installation.rst:250 msgid "2022.0" msgstr "" #: ../how-to/installation.rst:251 msgid "19" msgstr "" #: ../how-to/installation.rst:251 msgid "OpenSUSE" msgstr "" #: ../how-to/installation.rst:252 msgid "NAG" msgstr "" #: ../how-to/installation.rst:252 msgid "7.1" msgstr "" #: ../how-to/installation.rst:252 msgid "RHEL" msgstr "" #: ../how-to/installation.rst:255 msgid "" "Compiler known to fail are documented here, together with the last commit" " where this behaviour was encountered. If available an issue in on the " "projects issue tracker or the issue tracker of the dependencies is " "linked. Usually, it safe to assume that older versions of the same " "compiler will fail to compile as well and this failure is consistent over" " platforms and/or architectures." msgstr "" "Compiler die aktuell fehlschlagen sind hier dokumentiert, zusammen mit " "dem letzten Commit, an dem dieses Verhalten aufgetreten ist. Wenn " "verfügbar ist ein Problem im Issue-Tracker des Projekts oder der Issue-" "Tracker der Abhängigkeiten verlinkt. Normalerweise ist es sicher, dass " "ältere Versionen desselben Compilers auch fehlschlagen und dieses " "Verhalten ist über Platformen und/oder Architekturen konsistent." #: ../how-to/installation.rst:260 msgid "Reference" msgstr "Referenz" #: ../how-to/installation.rst:262 msgid "Flang" msgstr "" #: ../how-to/installation.rst:262 msgid "20190329" msgstr "" #: ../how-to/installation.rst:262 msgid "`f066ec6`_, `toml-f#28`_" msgstr "" #: ../how-to/installation.rst:263 msgid "NVHPC" msgstr "" #: ../how-to/installation.rst:263 msgid "20.9" msgstr "" #: ../how-to/installation.rst:263 msgid "Manjaro Linux" msgstr "" #: ../how-to/installation.rst:263 msgid "`f066ec6`_, `toml-f#27`_" msgstr "" #: ../how-to/integration.rst:4 msgid "Using TOML Fortran" msgstr "TOML Fortran verwenden" #: ../how-to/integration.rst:6 msgid "" "This tutorial shows how to integrate the TOML Fortran library with your " "build system and use it easily in your project." msgstr "" "Dieses Tutorial zeigt, wie sich die TOML Fortran-Bibliothek mit Deinem " "Build-System integrieren kannst und sie einfach in Dein Projekt " "verwendest." #: ../how-to/integration.rst:10 msgid "Using the Fortran package manager" msgstr "Mit dem Fortran-Paket-Manager verwenden" #: ../how-to/integration.rst:12 msgid "" "The Fortran package manager (`fpm `_) is a " "tool for building Fortran projects and managing dependencies on Fortran " "libraries. To enable TOML Fortran in your fpm project add the following " "entry to your package manifest:" msgstr "" "Der Fortran-Paket-Manager (`fpm `_) ist " "ein Werkzeug zum Erstellen von Fortran-Projekten und Verwaltung von " "Abhängigkeiten von Fortran-Bibliotheken. Um TOML Fortran in Deinem fpm-" "Projekt zu aktivieren, füge folgenden Eintrag in Deinen Paket-Manifest " "ein:" #: ../how-to/integration.rst:21 msgid "" "When building your project fpm will automatically fetch TOML Fortran for " "you and build it as part of your project. The TOML Fortran modules become" " useable in your project." msgstr "" "Wenn Du Dein Projekt baust, wird fpm automatisch TOML Fortran für Dich " "herunterladen und als Teil Deines Projekts kompilieren. Die TOML Fortran-" "Module werden in Deinem Projekt dann verfügbar gemacht." #: ../how-to/integration.rst:26 msgid "Integrate with meson" msgstr "Mit meson integrieren" #: ../how-to/integration.rst:28 msgid "" "To allow meson to use TOML Fortran it is easiest to include it as " "subproject using a git wrap file placed in the ``subprojects`` directory." msgstr "" "Um TOML Fortran in meson zu verwenden, ist es am einfachsten, wenn Du es " "als Unterprojekt mit einer git-Wrap-Datei im ``subprojects``-Verzeichnis " "einbindest." #: ../how-to/integration.rst:30 msgid "subprojects/toml-f.wrap" msgstr "" #: ../how-to/integration.rst:38 msgid "" "The revision can be adjusted to pin a specific release tag of TOML " "Fortran for additional stability. In the projects meson buid file the " "dependency method can be used to access TOML Fortran using the wrap file " "to define a fallback which is built on-demand." msgstr "" "Die Revision kann angepasst werden, um eine bestimmte Release-Tag von " "TOML Fortran festzulegen und die Stabilität zu erhöhen. In den Meson " "Build-Dateien kannst Du dann TOML Fortran als Abhängigkeit verwenden, " "dank der Wrap-Datei, wird eine Fallback-Version definiert und TOML " "Fortran kann bei Bedarf mitzukompiliert werden." #: ../how-to/integration.rst:41 msgid "meson.build" msgstr "" #: ../how-to/integration.rst:52 msgid "" "Finally, you can add ``tomlf_dep`` as dependency to any of your targets " "and are done." msgstr "" "Damit kannst Du ``tomlf_dep`` als Abhängigkeit zu einem beliebigen Ziel " "hinzufügen und bist fertig." #: ../how-to/integration.rst:56 msgid "Integrate with CMake" msgstr "Mit CMake integrieren" #: ../how-to/integration.rst:58 msgid "" "To use TOML Fortran in CMake based projects it is useful to define your " "own find-module, to allow on-demand compilation of TOML Fortran as well " "as discovery of installed packages from both meson and CMake based " "builds." msgstr "" "Um TOML Fortran in CMake-basierten Projekten zu verwenden, ist es " "nützlich, ein eigenes find-Modul zu definieren, um TOML Fortran entweder " "bei Bedarf mitzukompiliert oder auch die Installation von installierten " "Paketen von sowohl meson als auch CMake-basierten Builds zu ermöglichen." #: ../how-to/integration.rst:68 msgid "" "In your main CMake build file you have to include the custom find-module " "in your ``CMAKE_MODULE_PATH``, afterwards you can just use " "``find_package`` to obtain the ``toml-f::toml-f`` target and link against" " it." msgstr "" "In Deiner primären CMake-Build Datei musst Du das eigene find-Modul in " "Deinem ``CMAKE_MODULE_PATH`` einbinden, danach kannst Du ``find_package``" " verwenden, um das ``toml-f::toml-f``-Ziel zu erhalten und dagegen zu " "verlinken." #: ../how-to/integration.rst:70 msgid "CMakeLists.txt" msgstr "" #: ../how-to/integration.rst:111 msgid "" "Note that we also install the find-modules, this is important if you want" " to make your CMake projects reusable in the same way TOML Fortran can be" " used in your project. Finally we need some boilerplate to define the " "custom find-module is documented below." msgstr "" "Beachte, dass wir auch die find-Module installieren, das ist wichtig, " "wenn Du Deine CMake-Projekte in der gleichen Weise wie TOML Fortran in " "anderen Projekt verwenden kannst. Zuletzt brauchst Du ein paar Zeilen " "Code, um das eigene find-Modul zu definieren, siehe unten." #: ../how-to/integration.rst:115 msgid "Imported Targets" msgstr "Importierte Ziele" #: ../how-to/integration.rst:117 msgid "This module provides the following imported target, if found:" msgstr "" "Dieses Modul stellt folgende importierte Ziele bereit, falls es gefunden " "wurde:" #: ../how-to/integration.rst:121 msgid "``toml-f::toml-f``" msgstr "" #: ../how-to/integration.rst:120 msgid "The toml-f library" msgstr "Die toml-f Bibliothek" #: ../how-to/integration.rst:124 msgid "Result Variables" msgstr "Ergebnisvariablen" #: ../how-to/integration.rst:126 msgid "This module will define the following variables:" msgstr "Dieses Modul definiert folgende Variablen:" #: ../how-to/integration.rst:129 msgid "``TOML_FORTRAN_FOUND``" msgstr "" #: ../how-to/integration.rst:129 msgid "True if the toml-f library is available" msgstr "Wahr wenn die toml-f Bibliothek verfügbar ist" #: ../how-to/integration.rst:133 msgid "``TOML_FORTRAN_SOURCE_DIR``" msgstr "" #: ../how-to/integration.rst:132 msgid "" "Path to the source directory of the toml-f project, only set if the " "project is included as source." msgstr "" "Pfad zum Quellverzeichnis des toml-f Projekts, nur gesetzt, wenn das " "Projekt als Quellcode eingebunden wird." #: ../how-to/integration.rst:137 msgid "``TOML_FORTRAN_BINARY_DIR``" msgstr "" #: ../how-to/integration.rst:136 msgid "" "Path to the binary directory of the toml-f project, only set if the " "project is included as source." msgstr "" "Pfad zum Buildverzeichnis des toml-f Projekts, nur gesetzt, wenn das " "Projekt als Quellcode eingebunden wird." #: ../how-to/integration.rst:140 msgid "Cache variables" msgstr "Cachevariablen" #: ../how-to/integration.rst:142 msgid "" "The following cache variables may be set to influence the library " "detection:" msgstr "" "Die folgenden Cachevariablen können gesetzt werden um die Einbindung der " "Bibliothek zu beeinflussen:" #: ../how-to/integration.rst:150 msgid "``TOML_FORTRAN_FIND_METHOD``" msgstr "" #: ../how-to/integration.rst:145 msgid "Methods to find or make the project available. Available methods are" msgstr "Möglichkeiten zum Finden oder zum Erstellen des Projekts. Verfügbar sind " #: ../how-to/integration.rst:147 msgid "``cmake``: Try to find via CMake config file" msgstr "``cmake``: Versuche via CMake Konfigurationsdatei zu finden" #: ../how-to/integration.rst:148 msgid "``pkgconf``: Try to find via pkg-config file" msgstr "``pkgconf``: Versuche via pkg-config Datei zu finden" #: ../how-to/integration.rst:149 msgid "``subproject``: Use source in subprojects directory" msgstr "``subproject``: Verwende Quellcode in Unterprojektverzeichnis" #: ../how-to/integration.rst:150 msgid "``fetch``: Fetch the source from upstream" msgstr "``fetch``: Hole den Quellcode vom Upstream Repository" #: ../how-to/integration.rst:153 msgid "``TOML_FORTRAN_DIR``" msgstr "" #: ../how-to/integration.rst:153 msgid "Used for searching the CMake config file" msgstr "Wird für die Suche der CMake Konfigurationsdatei verwendet" #: ../how-to/integration.rst:156 msgid "``TOML_FORTRAN_SUBPROJECT``" msgstr "" #: ../how-to/integration.rst:156 msgid "Directory to find the toml-f subproject, relative to the project root" msgstr "" "Verzeichnis zum Finden des toml-f Unterprojekts, relativ zum Projekt " "Wurzelverzeichnis" #: ../how-to/integration.rst:158 msgid "cmake/Findtoml-f.cmake" msgstr "" #: ../how-to/integration.rst:280 msgid "Other build systems" msgstr "Andere Buildsysteme" #: ../how-to/integration.rst:282 msgid "" "Other build systems must discover a precompiled TOML Fortran library from" " the system. For this purpose the ``pkg-config`` tool is used. After " "installing TOML Fortran with either meson or CMake a pc-file is generated" " which can be discovered by ``pkg-config`` and describes how to compile " "against the installed module files as well as link against the TOML " "Fortran library. First check if the ``pkg-config`` tool is available and " "can discover TOML Fortran" msgstr "" "Andere Buildsysteme müssen eine vorkompilierte TOML Fortran Bibliothek " "auf dem System finden und nutzen. Dazu wird das ``pkg-config`` Tool " "verwendet. Nach der Installation von TOML Fortran mit Meson oder CMake " "wird eine pc-Datei erzeugt, die von ``pkg-config`` gefunden werden kann " "und beschreibt, wie gegen die installierten Moduldateien kompiliert " "werden kann und wie sich die TOML Fortran Bibliothek verlinken " "lässt.Zunächst prüfe, ob das ``pkg-config`` Tool verfügbar ist und TOML " "Fortran gefunden werden kann." #: ../how-to/integration.rst:291 msgid "" "Make sure to adjust the ``PKG_CONFIG_PATH`` environment variable to point" " to the correct installation directory. Using the ``--libs`` and " "``--cflags`` options the libraries to link against as well as the include" " directories can be obtained:" msgstr "" "Stelle sicher, dass die Umgebungsvariable ``PKG_CONFIG_PATH`` auf das " "richtige Installationsverzeichnis zeigt. Mit den ``--libs`` und " "``--cflags`` Optionen können die Bibliotheken, die verlinkt werden " "sollen, und die Include-Verzeichnisse erhalten werden:" #: ../how-to/integration.rst:299 msgid "In a handwritten Makefile those can be included by" msgstr "In einem handgeschriebenen Makefile können diese wie folgt genutzt werden" #: ../how-to/serde.rst:2 msgid "Serializable base class" msgstr "Serialisierbare Basisklasse" #: ../how-to/serde.rst:4 msgid "" "This recipe shows how to create a serializable class based on TOML " "Fortran. Currently, TOML Fortran does not define such a base class " "itself, therefore we define a loader and dumper interface for turning a " "file or connected unit into a data structure. The abstract base class " "will implement the processing of the file or unit to a TOML data " "structure and pass it to a deferred procedure which the implementing " "class uses to define its mapping from and back to the TOML data " "structure. This way an easily round-tripable data structure can be " "created and used in a variety of contexts." msgstr "" "Dieses Rezept zeigt, wie eine serialisierbare Klasse auf TOML Fortran " "basierend konstruiert werden kann. Momentan ist TOML Fortran selbst keine" " Klasse als Basis für eine serialisierbare Klasse, daher werden wir eine " "Loader- und Dumper-Schnittstelle definieren, um eine Datei oder eine " "verbundene Unit in eine Datenstruktur zu konvertieren. Die abstrakte " "Basisklasse implementiert die Verarbeitung der Datei oder der Unit in " "eine TOML Datenstruktur und leitet diese an eine deferred Prozedur " "weiter, die in der Klasse implementiert wird um, ein Mapping von der TOML" " Datenstruktur and zurück zu definieren. Diese Art von Datenstruktur kann" " in verschiedenen Kontexten genutzt werden and einfach in TOML und zurück" " transferiert werden." #: ../how-to/serde.rst:11 msgid "" "TOML Fortran might provide such abstract base class in the future " "natively." msgstr "" "TOML Fortran könnte in Zukunft selbst eine solche abstrakte Basisklasse " "bereitstellen." #: ../how-to/serde.rst:13 msgid "The base class can be defined as" msgstr "Die Basisklasse kann wie folgt definiert werden" #: ../how-to/serde.rst:15 msgid "src/serde_class.f90" msgstr "" #: ../how-to/serde.rst:19 msgid "" "We also define a convenience error handler which holds the error message " "and signals its error status by its allocation state." msgstr "" "Wir definieren auch einen Fehler-Handler, der die Fehlermeldung speichert" " und den Fehlerstatus durch seinen Allokationstatus signalisiert." #: ../how-to/serde.rst:21 msgid "src/serde_error.f90" msgstr "" #: ../how-to/serde.rst:25 msgid "" "An example for a serializable class based on the above base class is " "given below." msgstr "" "Ein Beispiel für eine serialisierbare Klasse basierend auf der oben " "gegebenen Basisklasse ist unten aufgeführt." #: ../how-to/serde.rst:27 msgid "src/demo.f90" msgstr "" #: ../how-to/serde.rst:31 msgid "" "The defined data class can in an application easily be loaded from a " "file, while the actual implementation does not have to deal with getting " "the TOML data structure from the file but can assume that if the " "configuration file was valid TOML it will be provided with a data " "structure to read from." msgstr "" "Die definierte Datenklasse kann in einer Anwendung einfach aus einer " "Datei geladen werden, während die tatsächliche Implementierung sich nicht" " um das Lesen der TOML Datenstruktur kümmert, sondern kann sich auch " "darauf verlassen, dass wenn die Konfigurationsdatei gültiges TOML war, " "eine Datenstruktur zum Lesen bereitsteht." #: ../how-to/table.rst:2 msgid "Working with tables" msgstr "Mit Tabellen zu arbeiten" #: ../how-to/table.rst:4 msgid "" "The central data structures in TOML are tables, they contain a map from a" " key (string) to any supported data type in TOML. These recipes describe " "common scenarios for retrieving data from tables using the TOML Fortran " "library." msgstr "" "Die zentrale Datenstruktur in TOML sind Tabellen, sie enthalten eine " "Zuordnung von Einträgen (String) zu allen unterstützten Datentypen in " "TOML. Dieses Rezept beschreibt die allgemeinen Szenarien für das Abfragen" " von Daten aus Tabellen mit der TOML Fortran-Bibliothek." #: ../how-to/table.rst:9 msgid "Accessing nested tables" msgstr "Auf geschachtelte Tabellen zuzugreifen" #: ../how-to/table.rst:11 msgid "" "Using nested tables provides the possibility to better group " "configuration data. Since the TOML format always requires the full " "qualified path in each table header, it is easy for the user to identify " "where the current settings belong to. On the other hand, deeply nested " "tables with long table paths or path components make them more difficult " "to use and a good balance of short and expressive table names and " "meaningful subtables is required." msgstr "" "Geschachtelte Tabellen ermöglichen es, Konfigurationsdaten besser zu " "gruppieren. Da das TOML-Format immer den vollständigen Pfad in jeder " "Tabellenkopfangabe erfordert, ist es einfach für den Benutzer zu " "erkennen, wo die aktuellen Einstellungen zu finden sind. Aber schwieriger" " wird es wenn tief geschachtelte Tabellen mit langen Tabellenpfaden oder " "Pfadkomponenten verwendet werden. Eine gute Balance zwischen kurzen und " "aussagekräftigen Tabellennamen und Untereinträge ist erforderlich." #: ../how-to/table.rst:15 msgid "" "An example of an electronic structure code implementing different " "Hamiltonians is given below." msgstr "" "Ein Beispiel für einen elektronischen Strukturcode, der unterschiedliche " "Hamiltonians implementiert, ist unten aufgeführt." #: ../how-to/table.rst:26 msgid "" "The deepest nested subtable with entries in this example is the " "*hamiltonian.dftb.skf* path." msgstr "" "Die tiefste geschachtelte Untertabelle in diesem Beispiel ist der " "*hamiltonian.dftb.skf* Pfad." #: ../how-to/table.rst:28 msgid "" "Such layout in the configuration file will usually be mirrored in the " "actual implementation, with every table corresponding to a derived type " "describing the input. For the example above in total six derived types " "for the individual tables are defined as" msgstr "" "Dieser Aufbau in der Konfigurationsdatei wird in der tatsächlichen " "Implementierung korrespondierend wiedergeben, wobei jede Tabelle " "entsprechend einen abgeleiteten Typ beschreibt, der der Eingabe " "entspricht. Für das Beispiel oben sind insgesamt sechs abgeleitete Typen " "für die einzelnen Tabellen definiert." #: ../how-to/table.rst:31 ../how-to/table.rst:51 ../how-to/table.rst:58 #: ../how-to/table.rst:67 ../how-to/table.rst:84 msgid "src/input.f90" msgstr "" #: ../how-to/table.rst:38 #, python-format msgid "" "The representation in Fortran derived types looks lengthy compared to the" " actual TOML input. Consider that the 40 lines of Fortran code contain " "50% comments describing the data types briefly for (future) developers. " "Of course, the user documentation of the input format will be much more " "extensive, containing descriptions for every table and every entry, " "including input ranges and unit conventions. The final input file " "provided by the user can be brief and expressive." msgstr "" "Die Darstellung in abgeleiteten Typen ist in Fortran länger als die TOML-" "Eingabe. Dabei sollte beachten werden, dass die 40 Zeilen von Fortran " "Code 50% Kommentare enthalten, die die Datentypen für (zukünftige) " "Entwickler beschreiben. Zudem ist die Benutzerdokumentation des " "Eingabeformats ähnlich ausführlich, mit Beschreibungen für alle Tabellen " "und Einträge, inklusive Eingabebereichs und Einheiten. Die Eingabedatei " "kann dann kurz und aussagekräftig sein." #: ../how-to/table.rst:43 msgid "" "Staring with the root of the table which is read in the " "*simulation_input* there are two ways to obtain access to a subtable, " "first we get the *hamiltonian* subtable, which we defined as mandatory, " "using the ``get_value`` interface. In case it is present a reference will" " be returned in the *child* pointer. If no table is available in the " "input TOML Fortran will insert it into the root table and return the " "reference to the newly created table. The *child* pointer can still be " "unassigned in case invalid input is provided, which will result in " "raising an error in the implementation shown below." msgstr "" "Mit dem Wurzelknoten der Tabelle, der in den Datentypen " "*simulation_input* gelesen wird, gibt es zwei Wege, eine Untertabelle " "abzufragen, zunächst wird die *hamiltonian* Untertabelle abgefragt, die " "als benötigt definiert ist und mit der ``get_value``-Schnittstelle " "abgefragt wird. In Fällen, wenn diese Tabelle vorhanden ist, wird eine " "Referenz mit dem *child* Zeiger zurückgegeben. Falls keine Tabelle " "vorhanden ist, wird sie in den Wurzelknoten eingefügt und eine Referenz " "zurückgegeben. Der *child* Zeiger kann in Fällen von unzulässigen " "Eingaben nicht zugewiesen werden, was mit einer Fehlermeldung in der " "Implementierung abgefangen wird." #: ../how-to/table.rst:48 msgid "" "The alternative is to explicitly mark the subtable as optional, like for " "the *analysis* table, if no table is available or the entry is invalid " "the *child* pointer will not be assigned. To differentiate those cases we" " can request the status information, check whether the operation was " "successful, and cleanly handle the error case." msgstr "" "Alternativ kann die Untertabelle als optional markiert werden, wie für " "die *analysis* Tabelle, wenn keine Tabelle vorhanden ist oder der Eintrag" " ungültig ist, bleibt der *child* Zeiger unzugewiesen. Um diese Fälle zu " "unterscheiden, kann der Statusinformationen abgefragt werden, um zu " "prüfen, ob die Operation erfolgreich war, und im Fall eines " "Eingabefehlers eine Fehlermeldung ausgegeben wird." #: ../how-to/table.rst:56 msgid "" "The same happens for reading the *hamiltonian_input* and *dftb_input* " "entry." msgstr "Das gleiche gilt für die *hamiltonian_input* und *dftb_input* Einträge." #: ../how-to/table.rst:63 msgid "" "Finally, we can implement reading the terminal subtables into the " "*scc_input*, *skf_input*, and *analysis_input*, where we retrieve the " "actual values using the ``get_value`` interface. Note that we can " "conveniently define default values using the ``get_value`` interface. For" " proper error handling, we can retrieve the optional *stat* argument as " "well." msgstr "" "Zuletzt können die terminalen Untertabellen in *scc_input*, *skf_input*, " "und *analysis_input* gelesen werden, wo die Werte mit ``get_value`` " "abgefragt werden. Zusätzlich kann ein Standardwert mit ``get_value`` " "definiert werden. Für einen korrekten Fehlerbehandlung kann auch der " "optionalen *stat*-Parameter abgefragt werden." #: ../how-to/table.rst:72 msgid "" "For the small incomplete input as shown here, the fine-grained " "substructure seems overengineered and could be fully defined in the " "reading routine for the document root as well. However, for larger " "program inputs such a structure can help to ensure that input readers are" " properly modular and reusable." msgstr "" "Für die kleine unvollständige Eingabe wie hier sieht die fein unterteile " "Struktur zu vielen Unterstrukturen übermäßig kompliziert aus und könnte " "auch in der Lese-Routine für der Dokument-Wurzel definiert werden. " "Allerdings kann für größere Programm-Eingaben eine solche Struktur " "hilfreich sein, um sicherzustellen, dass die Lese-Routinen modular und " "wiederverwendbar sind." #: ../how-to/table.rst:77 msgid "" "The allocation status of a component of the derived type can be used " "instead of a separate boolean flag to indicate whether a feature should " "be activated. This avoids requiring conditional code inside a reader " "routine for conditionally handling entries depending on a boolean flag, " "instead they can be collected in a subtable." msgstr "" "Der Allokationsstatus der Teilkomponenten in den Datentypen kann statt " "eines separated logischen Option verwendet werden, um eine aktive " "Funktionalität zu signalisieren. Dies ermöglicht es auf zusätzliche Logik" " zu verzichten um abhängige Einträge zu lesen, diese sind stattdessen in " "einer Untertabelle gesammelt." #: ../how-to/table.rst msgid "Full source code" msgstr "Vollständiger Quellcode" #: ../how-to/table.rst:82 msgid "The full module implementing the *simulation_input* reading" msgstr "Die vollständige Modul-Implementierung des *simulation_input*" #: ../how-to/table.rst:88 ../how-to/table.rst:185 msgid "The auxiliary module providing the error handler" msgstr "Hilfsmodul um Fehlerbehandlung zu ermöglichen" #: ../how-to/table.rst:90 ../how-to/table.rst:187 msgid "src/error.f90" msgstr "" #: ../how-to/table.rst:96 msgid "Direct access via key paths" msgstr "Direkter Zugriff über Schlüsselpfad" #: ../how-to/table.rst:98 msgid "" "If only a deeply nested value of a data structure is needed it can be " "retrieved by using a key path. The build interface will internally walk " "the key path, resolve the child tables and create them as necessary." msgstr "" "Wenn nur ein tiefgeschachtelter Wert einer Datenstruktur benötigt wird, " "kann dieser über einen Schlüsselpfad abgefragt werden. Im Build-Interface" " wird intern der Schlüsselpfad aufgebaut, Untertabellen aufgelöst, und " "wenn nötig erstellt." #: ../how-to/table.rst:103 msgid "" "Repeatly accessing values via a key path from the document root, rather " "than retrieving the reference the desired child table, will introduce an " "overhead each time the key path is resolved." msgstr "" "Wiederholter Zugriff auf Werte über einen Schlüsselpfad von der Dokument-" "wurzel, anstelle von der Referenz auf die gewünschte Untertabelle, " "braucht zusätzliche Ressourcen, da der Schlüsselpfad jedes Mal erneut " "aufgelöst wird." #: ../how-to/table.rst:105 msgid "" "For the previous example we can use the key path access to retrieve the " "most deeply nested value as shown below." msgstr "" "Für das vorherigen Beispiel kann der Schlüsselpfad zum Abfragen des " "am tiefsten geschachtelten Wertes wie unten angegeben benutzt werden." #: ../how-to/table.rst:116 msgid "" "Similar like other build interfaces it can be used to create the " "subtables as well as the string value by providing a default." msgstr "" "Ähnlich wie andere Build-Interfaces kann auch ein Standardwert angegeben " "werden, um die Untertabelle zu erstellen." #: ../how-to/table.rst:120 msgid "Iterating over keys" msgstr "Iterieren über Einträge" #: ../how-to/table.rst:122 msgid "" "An expressive way to organize data is by providing a table where the keys" " of each entry describe the object that should be initialized. For " "example in a package manager, the keys represent the dependency, where " "each dependency is declared in a subtable. Furthermore, a convenience " "feature might be the possibility to just provide a string, which is " "interpreted as a version subentry." msgstr "" "Eine klare Art Daten zu organisieren ist durch die Verwendung einer " "Tabelle in der die Einträge die Initialisierung der Objekte beschreiben. " "Zum Beispiel in einem Paketmanager beschreiben die Einträge die " "Abhängigkeiten, in der jede davon in einer Untertabelle deklariert wird. " "Zusätzlich kann der einfachheitshalber ein String angeben werden, der als" " Versions-Einschränkung für die Untertabelle interpretiert wird." #: ../how-to/table.rst:126 msgid "" "The final usage of this in a *requirements* table could look like the " "snippet shown below." msgstr "" "Die Implementierung in einer *requirements* Tabelle könnte wie folgt " "aussehen:" #: ../how-to/table.rst:138 msgid "" "The first three entries provide a string value, while the fourth entry " "provides a subtable implicitly by using dotted key-value pairs and the " "last entry uses an inline table." msgstr "" "In den ersten drei Einträgen wird ein String Wert verwendet, während der " "vierte Eintrag eine implizite Untertabelle über ein Punktgetrenntes " "Werte-Paar nutzt und der letzte Eintrag eine Inline-Tabelle ist." #: ../how-to/table.rst:140 msgid "" "Here we want to focus on the iteration and the default initialization, " "the internal structure of the *requirement_type* is secondary for this " "example. We provide the minimal definition only holding the name of the " "dependency for demonstration purposes." msgstr "" "Wir wollen uns hier auf die Iteration und die Standardinitialisierung " "konzentrieren. Die internen Struktur des *requirement_type* Datentype ist" " nebensächlich für dieses Beispiel. Daher geben wir für dieses Beispiel " "nur die minimale Definition an, die ausschließlich den Namen der " "Abhängigkeiten enthält." #: ../how-to/table.rst:143 ../how-to/table.rst:153 ../how-to/table.rst:168 #: ../how-to/table.rst:181 msgid "src/requirements.f90" msgstr "" #: ../how-to/table.rst:148 msgid "" "For the actual implementation of reading all entries from the table, we " "will use a one-dimensional array of *requirement_type* values. Using the " "``get_keys`` method of the table we can obtain a list of all keys for the" " current table, the method will always allocate the ``list`` variable and" " we can safely allocate the *requirement_type* using the number of keys. " "To obtain the subtable, the ``get_value`` interface can be used, it will " "return a pointer to the subtable, either created implicitly by using a " "dotted key-value pair or by an inline table as shown in the snippet " "above. Finally, we can call the actual constructor of the " "*requirement_type* using the subtable references with the ``child`` " "pointer." msgstr "" "Für die Implementierung der Leseoperationen wird ein eindimensionales " "Feld von *requirement_type* Datentypen verwendet. Durch die ``get_keys`` " "Methode der Tabelle können alle Schlüssel der aktuellen Tabelle ermittelt" " werden. Die Methode wird immer eine allozierte ``list`` Variable " "zurückgeben und kann damit das *requirement_type* Feld auf die Anzahl der" " Schlüssel allozieren. Die ``get_value`` Methode kann verwendet werden, " "um auf die Untertabelle zuzugreifen. Sie wird einen Zeiger auf die " "Untertabelle zurückgeben, die entweder durch ein Punktgetrenntes Wert-" "Paar oder eine Inline-Tabelle erstellt wurde. Anschließend kann der " "Konstruktor des *requirement_type* Datentyps auf die Untertabelle-" "Referenzen mit dem ``child`` Zeiger aufgerufen werden." #: ../how-to/table.rst:158 msgid "" "The other scenario we want to support is the presence of a string rather " "than a subtable. In this case, the ``get_value`` interface will fail, " "while it provides an optional status argument to check for successful " "operation, we can more conveniently and idiomatically verify the success " "by checking the state of the ``child`` pointer. If there is no subtable " "to reference, *i.e.* because it is a key-value pair with a string entry, " "the ``child`` pointer will not be associated, which can be easily " "checked. For this case we will again use the ``get_value`` interface, but" " this time to retrieve the entry into a deferred length character. Again " "we can idiomatically check the status of the operation using the " "allocation state of the variable and create the appropriate error message" " if needed. Eventually, we have to provide the constructor of the " "requirements with a table, for this purpose we create a dummy table and " "set the entry at the version key to the just retrieved string. The newly " "created dummy table can be associated with the ``child`` pointer and " "passed to the actual constructor." msgstr "" "Der zweite Scenario, das wir unterstützen, ist die Nutzung eines Strings " "anstatt einer Untertabelle. In diesem Fall wird die ``get_value`` Methode" " fehlschlagen, während ein optionaler Status-Parameter zurückgeben werden" " kann, um den Erfolg zu prüfen, können wir einfacher und idomatisch den " "Erfolg am Zuweisungs-Status des ``child`` Zeigers prüfen. Wenn es keine " "Untertabelle zu referenzieren gibt, weil es ein Wert-Paar mit einem " "String-Eintrag ist, wird der ``child`` Zeiger nicht zugewiesen. Dies kann" " einfach geprüft werden. Für diesen Fall werden wir die ``get_value`` " "Methode noch einmal verwenden, um den Eintrag in einen dynamischen String" " zu speichern. Auch hier können wir den Erfolg am Zuweisungs-Status " "prüfen und wenn ein Fehler aufgetreten ist, wird eine Fehlermeldung " "erzeugt. Anschließend können wir den Konstruktor des *requirement_type* " "Datentyps mit einer Dummy Tabelle aufrufen auf die wir mit dem ``child`` " "Zeiger referenziert haben." #: ../how-to/table.rst:166 msgid "" "The actual constructor for our example is very minimalistic and only " "recovers the name of the dependency which is passed as a separate " "argument." msgstr "" "Der eigentliche Konstruktor für dieses Beispiel ist sehr minimalistisch " "und spezifiziert nur den Namen der Abhängigkeit, welcher als separates " "Argument übergeben wird." #: ../how-to/table.rst:175 msgid "" "While we provide an error handler in the example, we also ensure that the" " allocation status of the *requirement_type* values communicates the " "status of the operation as well." msgstr "" "Wir bieten einen Fehlerbehandlungs-Prozess in diesem Beispiel, zusätzlich" " stellen wir sicher, dass der Zuweisungs-Status des *requirement_type* " "Feldes die Status-Informationen der Operation ebenfalls kommuniziert." #: ../how-to/table.rst:179 msgid "The full module implementing the *requirement_type* reading" msgstr "Das vollständige Modul zum Einlesen des *requirement_type*" #: ../how-to/table.rst:193 msgid "Array of tables" msgstr "Feld von Tabellen" #: ../how-to/table.rst:195 msgid "" "A special construct in TOML is the array of tables syntax, it provides a " "more verbose form to declare several tables in an array, which are " "usually provided using inline tables as shown below." msgstr "" "Ein spezielles Konstrukt in TOML ist ein Feld von Tabellen, das eine " "ausführlichere Form zum Deklarieren von mehreren Tabellen in einem Feld " "anbietet. Normalerweise wird dies durch Inline-Tabellen angegeben wie " "unten gezeigt." #: ../how-to/table.rst:206 msgid "" "Comparing the above example to the snippet below using an array of tables" " for the *tasks* array, the more verbose form becomes preferable in case " "further subtables are needed. Except for the subtables *config* the same " "data is provided." msgstr "" "Vergleichen wir das obige Beispiel mit dem unten gezeigten Beispiel für " "das *tasks* Feld, dann ist die ausführlichere Form bevorzugt, wenn wir " "weitere Untertabelle benötigen. In beiden Beispielen werden bis auf die " "*config* Untertabelle die gleichen Daten angegeben." #: ../how-to/table.rst:234 msgid "" "To represent this data we can use a single *task_config* derived type " "with a polymorphic *driver_config* member identifying the actual task. " "For this example, we will have two implementations of such tasks such as " "LBFGS and Velocity Verlet, which are defined in the following snippets." msgstr "" "Um diese Daten zu repräsentieren können wir einen eigenen *task_config* " "Datentyp verwenden, welcher einen polymorphen *driver_config* Eintrag " "enthält, der die tatsächliche Aufgabe identifiziert. Für dieses Beispiel " "werden zwei Implementierungen wie LBFGS und Velocity Verlet definiert, " "welche in den folgenden Codeblöcken angegeben sind." #: ../how-to/table.rst:237 ../how-to/table.rst:246 ../how-to/table.rst:260 #: ../how-to/table.rst:268 ../how-to/table.rst:279 msgid "src/task.f90" msgstr "" #: ../how-to/table.rst:242 msgid "" "To read the array of tables we start from the root document and fetch the" " *tasks* entry as an array using the ``get_value`` interface. The length " "of the full arrays is known and we can use it to allocate the list of " "*task_config* values before reading the individual entries. The " "individual tables inside the array can be addressed using the " "``get_value`` interface by passing the (one-based) index." msgstr "" "Um das Feld von Tabellen zu einlesen starten wir vom Wurzeldokument und " "holen das *tasks* Eintrag als Feld mit der ``get_value`` Routine.Die " "Länge des gesamten Feldes ist bekannt und wir können diese Information " "benutzen, um eine Liste von *task_config* Einträgen zu allokieren. Die " "Tabellen in dem Feld können mit der ``get_value`` Routine anhand der " "(1-basierten) Indexposition abgefragt werden." #: ../how-to/table.rst:253 msgid "" "In the setup above, if the *tasks* entry is not present it will be " "implicitly created as an empty array. The allocation and the loop over " "the entries will work, however the consuming code should check whether no" " tasks are meaningful or should produce an error." msgstr "" "Im obigen Setup wird das *tasks* Eintrag implizit erzeugt, wenn dieser " "nicht vorhanden ist. Die Allokation und der Schleifendurchlauf " "funktionieren ohne Probleme, allerdings müssen die folgenden Routinen " "prüfen, ob eine Simulation ohne Aufgaben Sinn hat oder einen Fehler " "erzeugen." #: ../how-to/table.rst:256 msgid "" "To read the individual tasks we define a separate procedure to make it " "easily reusable and hide the fact that we are working with a subtable. To" " make the task *name* optional we make it default to the driver name, for" " *allocatable* or *pointer* variables the exit status of ``get_value`` " "can be easily checked by the allocation or association status of the " "respective variable, alternatively an integer variable can be passed to " "the optional *stat* argument. Finally, the configuration reader is called" " depending on the value of *driver* for ease of usage we use a block " "construct to allocate the specific type and then transfer it using " "*move_alloc* into the *task_config*." msgstr "" "Für die einzelnen Aufgaben definieren wir eine separate Routine, um das " "Auslesen von Untertabellen zu vereinfachen und die Logik zu verstecken, " "dass wir mit einer Untertabelle arbeiten. Für den *name* Eintrag ist der " "Defaultwert der Name des Treibers. Für *allocatable* oder *pointer* " "Variablen kann der Exit-Status der ``get_value`` Routine direkt überprüft" " werden. Alternativ kann ein Integerwert als optionales *stat* Argument " "übergeben werden. Anschließend wird eine weitere Lese-Routine abhängig " "von dem *driver* Eintrag aufgerufen. Dabei nutzen wir einen Block, um die" " spezifischen Typen zu allokieren und diese mit *move_alloc* in die " "*task_config* Instanz zu transferieren." #: ../how-to/table.rst:265 msgid "" "For reading the actual driver configuration we use the ``get_value`` " "interface to obtain the settings. We use the same defaulting mechanism as" " for the *name* entry here." msgstr "" "Für die eigentliche Treiber-Konfiguration nutzen wir die ``get_value`` " "Routine, um die Einstellungen zu lesen. Wir nutzen dieselbe Defaultwert " "Mechanismus wie für den *name* Eintrag hier." #: ../how-to/table.rst:273 msgid "" "Note that this example does not propagate back errors but directly calls " "*error stop*, for a more robust error reporting this can be changed by a " "small error handle or a context type." msgstr "" "Dieses Beispiel gibt keine Fehler zurück, sondern ruft direkt *error " "stop* auf, um eine robustere Fehlerbehandlung zu ermöglichen, kann dies " "durch einen kleineren Fehlerbehandlungs-Typ oder einen Kontexttyp " "implementiert werden." #: ../how-to/table.rst:277 msgid "The full module implementing the *task_config* reading" msgstr "Die vollständige Modul-Implementierung um *task_config* zu lesen" fortran-toml-0.5.0/doc/locales/de/LC_MESSAGES/external.po0000664000175000017500000000203315201541453023050 0ustar alastairalastair# Copyright (C) 2019-2022, Sebastian Ehlert # This file is distributed under the same license as the toml-f package. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: toml-f 0.2.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-04-10 20:16+0200\n" "PO-Revision-Date: 2022-04-10 20:16+0200\n" "Last-Translator: Sebastian Ehlert\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" #: ../external.rst:7 msgid "Modern Fortran by Philipp Engel (English)" msgstr "Modern Fortran von Philipp Engel (Englisch)" #: ../external.rst:7 msgid "Fortran in Action by Zuo Zhihua (Chinese)" msgstr "Fortran in Action von Zuo Zhihua (Chinesisch)" #: ../external.rst:2 msgid "External resources" msgstr "Externe Ressourcen" #: ../external.rst:4 msgid "" "Sites and blog posts with information on TOML Fortran are mentioned and " "linked here." msgstr "" "Seiten und Blog-Beiträge mit Informationen zu TOML Fortran werden hier " "angezeigt und verlinkt." fortran-toml-0.5.0/doc/locales/es/0000775000175000017500000000000015201541453017122 5ustar alastairalastairfortran-toml-0.5.0/doc/locales/es/LC_MESSAGES/0000775000175000017500000000000015201541453020707 5ustar alastairalastairfortran-toml-0.5.0/doc/locales/es/LC_MESSAGES/tutorial.po0000664000175000017500000002113015201541453023107 0ustar alastairalastair# SOME DESCRIPTIVE TITLE. # Copyright (C) 2019-2022, Sebastian Ehlert # This file is distributed under the same license as the toml-f package. # FIRST AUTHOR , 2022. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: toml-f 0.2.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-06-19 18:08-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Asdrubal Lozada-Blanco\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.10.3\n" #: ../doc/tutorial/getting-started.rst:2 msgid "Getting started" msgstr "Iniciando" #: ../doc/tutorial/getting-started.rst:4 msgid "" "This tutorial provides a gentle introduction to the use of TOML Fortran. " "It will deal with reading as well as creating TOML data structures using " "the high-level build interface and discuss how to obtain a data structure" " from a TOML document or turn a data structure to TOML document again." msgstr "" "Este tutorial proporciona una introducción amable para el uso de TOML Fortran. " "Tratará de la lectura y creacción de estructuras de datos TOML usando " "la interfaz de construcción de alto nivel y como obtener una estructura de datos " "desde un documento TOML o convertir una estrutura de datos en un documento TOML " "totalmente nuevo." #: ../doc/tutorial/getting-started.rst:7 msgid "" "For this project we will be working with fpm, however you can use any " "build tool you are familar with, checkout the :ref:`integration guide " "` to find a matching setup. We start with creating a minimal" " package manifest to use TOML Fortran in our fpm project." msgstr "" "En este proyecto trabajaremos con fpm, no obstante usted puede usar cualquier " "herramienta de construcción con la que esté familiarizado, consulte la :ref:`guía de integración " "` para encontrar una configuración coincidente. Comenzamos creando un manifiesto " " de paquete mínimo para usar TOML Fortran en nuestro proyecto fpm." #: ../doc/tutorial/getting-started.rst:10 msgid "fpm.toml" msgstr "" #: ../doc/tutorial/getting-started.rst:18 msgid "" "The public TOML Fortran API is defined in the ``tomlf`` module, we will " "only use this module for this entire course. The main data structures we " "are going to interact with are ``toml_table`` and ``toml_array`` " "instances, which we can conveniently manipulate with the generic " "interface ``get_value``." msgstr "" "La API pública TOML Fortran es definida en el módulo ``tomlf``, en este curso completo " "solo usaremos este módulo. Las estructuras de datos principales con las que interactuaremos son las instancias ``toml_table and ``toml_array``, que pueden ser convenientemente manipuladas con la " "la interfaz genérica ``get_value``." #: ../doc/tutorial/getting-started.rst:21 msgid "src/reader.f90" msgstr "" #: ../doc/tutorial/getting-started.rst:25 msgid "" "Note that we declare the TOML data structure as mutable, *i.e.* " "``intent(inout)`` rather than just ``intent(in)``, as the ``get_value`` " "interface can modify the data structure. We start with a simple test " "program which is not actually reading any TOML document, but just passing" " an empty table to our reader." msgstr "" "Note que definimos la estructura de datos TOML como mutable, es decir, " "``intent(inout)`` en lugar de solo ``intent(in)``, como la interfaz ``get_value`` " "puede modificar la estructura de datos. Iniciamos con un programa de prueba simple, que realmente " "no está leyendo ningún documento TOML, sino solo pasando una tabla vacía a nuesto lector." #: ../doc/tutorial/getting-started.rst:28 msgid "app/defaults.f90" msgstr "" #: ../doc/tutorial/getting-started.rst:32 msgid "" "The ``get_value`` interface for processing the TOML data structure " "ensures that the data structure is complete throughout the whole process " "of reading it and will add the requested nodes if there are not present " "or will fill them in with default values. Convince yourself that the " "empty table indeed changed while reading by passing a serializer to it." msgstr "" "La interfaz ``get_value`` para el procesamiento de la estructura de datos TOML " "asegura que la estructura de datos esté completa a lo largo del proceso " "de lectura y agregará los nodos solicitados sino están presentes o los completará con " "valores predeterminados. Verifique que la tabla vacía realmente cambió durante la lectura " "pasandole un serializador." #: ../doc/tutorial/getting-started.rst:51 msgid "" "This behavior is very convenient because it allows us to define our " "default values while defining how we read the TOML data structure." msgstr "" "Este comportamiento es muy conveniente porque nos permite definir nuestros " "valores predeterminados al definir como leemos la estructura de datos TOML." #: ../doc/tutorial/getting-started.rst:55 msgid "" "The ``get_value`` build interface is only one way of accessing the TOML " "data structure provided by TOML Fortran. It takes an opinionated approach" " towards reading and modifying the data structure, which is suitable the " "majority of applications." msgstr "" "La interfaz de construcción ``get_value`` es solo una forma de acceder a la " "estrutura de datos TOML proporcionado por TOML Fortran. Este toma una aproximación " "pragmática para la lectura y modificación de la estructura de datos, que es apropiada " "en la mayoria de las aplicaciones." #: ../doc/tutorial/getting-started.rst:58 msgid "Now we will actually read a TOML document and pass it to our reader." msgstr "Ahora leeremos realmente un documento TOML y pasaremos este a nuestro lector." #: ../doc/tutorial/getting-started.rst:60 msgid "input.toml" msgstr "" #: ../doc/tutorial/getting-started.rst:66 msgid "" "We adapt our command line driver to read the file ``input.toml`` and " "output the values as before" msgstr "" "Adaptamos nuestro controlador de linea de comandos para leer el archivo ``input.toml`` y " "muestra los valores como antes" #: ../doc/tutorial/getting-started.rst:68 msgid "app/readin.f90" msgstr "" #: ../doc/tutorial/getting-started.rst:72 msgid "" "Running the program with fpm shows that we were able to read the correct " "values from the document" msgstr "" "Ejecutar el programa con fpm muestra que pudimos leer los valores correctos desde el documento" #: ../doc/tutorial/getting-started.rst:81 msgid "" "You can again use the serializer to write the final data structure, if " "you want to check whether the ``get_value`` interface has added default " "values." msgstr "" "Usted puede volver a usar el serializador para escribir la estructura de datos final, si " "desea verficar si la interfaz ``get_value`` ha adicionado los valores predeterminados." #: ../doc/tutorial/getting-started.rst:85 msgid "" "In this tutorial you have learned out to read simple data structures from" " TOML documents. You can now" msgstr "" "En este tutorial usted ha aprendido a leer estructuras de datos desde documentos TOML. Ahora puede" #: ../doc/tutorial/getting-started.rst:88 msgid "define the logic to read data from TOML structures" msgstr "definir la lógica para leer datos desde estructuras TOML" #: ../doc/tutorial/getting-started.rst:89 msgid "provide default values in your parser as you define the input structure" msgstr "" "proporcione valores predeterminados en su analizador a medida que define la estructura de entrada" #: ../doc/tutorial/getting-started.rst:90 msgid "read an actual TOML document from a file" msgstr "leer un documento TOML real desde un archivo" #: ../doc/tutorial/getting-started.rst:91 msgid "write TOML documents from your data structures" msgstr "escribir documentos TOML desde sus estructuras de datos" #: ../doc/tutorial/index.rst:4 msgid "Tutorial courses" msgstr "Cursos tutoriales" #: ../doc/tutorial/index.rst:6 msgid "" "This section contains self-contained courses teaching the usage of TOML " "Fortran. Each tutorial is a full example for a specific application " "covering several aspects of the library." msgstr "" "Esta sección contiene cursos autocontenidos que enseñan como usar TOML " "Fortran. Cada tutorial es un ejemplo completo para una aplicación específica " "cubriendo múltiples aspectos de la biblioteca." #: ../doc/tutorial/index.rst:9 msgid "Contributions are welcome!" msgstr "¡Contribuciones son bienvenidas!" #: ../doc/tutorial/index.rst:12 msgid "" "If you have written on TOML Fortran and want to contribute to the " "documentation, feel free to fork the repository and submit a pull " "request." msgstr "" "Si usted ha escrito sobre TOML Fortran y desea contribuir a la " "documentación, siéntase libre de bifurcar el repositório y envie una solicitud de integración." fortran-toml-0.5.0/doc/locales/es/LC_MESSAGES/index.po0000664000175000017500000001002015201541453022347 0ustar alastairalastair# SOME DESCRIPTIVE TITLE. # Copyright (C) 2019-2022, Sebastian Ehlert # This file is distributed under the same license as the toml-f package. # FIRST AUTHOR , 2022. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: toml-f 0.2.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-06-19 18:08-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Asdrubal Lozada-Blanco\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.10.3\n" #: ../doc/index.rst:113 msgid "Tutorials" msgstr "Tutoriales" #: ../doc/index.rst:113 msgid "How-Tos" msgstr "¿Cómo?" #: ../doc/index.rst:113 msgid "References" msgstr "Referencias" #: ../doc/index.rst:4 msgid "TOML Fortran library" msgstr "Biblioteca TOML Fortran" msgid "TOML Fortran" msgstr "TOML Fortran" msgid "License" msgstr "Licencia" msgid "Version" msgstr "Versión" msgid "Continuous Integration" msgstr "Integración continua" msgid "API docs" msgstr "Documentos de la API" msgid "Documentation Status" msgstr "Estado de la documentación" msgid "Coverage" msgstr "Cobertura" #: ../doc/index.rst:35 msgid "" "This project provides a library to work with `TOML `_ " "files and data structures and allows to define data serialization and " "deserialization in Fortran. The currently supported TOML standard is " "`version 1.0.0 `__. The TOML Fortran project " "is hosted at GitHub at `toml-f/toml-f " "`__." msgstr "Este proyecto proporciona una biblioteca para trabajar con archivos `TOML `_ " "y estructuras de datos, y permite definir la serialización y deserialización de datos en Fortran." "El estándar TOML admitido actualmente es la `versión 1.0.0 `__. " "El proyecto TOML Fortran está hospedado en Github en `toml-f/toml-f " "`__" #: ../doc/index.rst:49 msgid ":octicon:`mortar-board` Tutorials" msgstr ":octicon:`mortar-board` Tutoriales" #: ../doc/index.rst:51 msgid "" "Guides and courses for using TOML with complete and self-contained " "examples." msgstr "" "Guías y cursos para usar TOML con ejemplos completos y autocontenidos" #: ../doc/index.rst:60 msgid ":octicon:`book` Recipes" msgstr ":octicon:`book` Recetas" #: ../doc/index.rst:62 msgid "Examples and recipes for solving common tasks with TOML Fortran." msgstr "Ejemplos y recetas para resolver tareas comunes con TOML Fortran." #: ../doc/index.rst:70 msgid ":octicon:`gear` Reference" msgstr ":octicon:`gear` Referencia" #: ../doc/index.rst:72 msgid "Generated documentation of all procedures and derived types available." msgstr "Documentación generada de todos los procedimientos y tipos derivados disponibles." #: ../doc/index.rst:81 msgid ":octicon:`arrow-down` Installation" msgstr ":octicon:`arrow-down` Instalación" #: ../doc/index.rst:83 msgid "Instructions for installing, updating or compiling TOML Fortran." msgstr "" "Instrucciones para la instalación, actualización o compilación de TOML Fortran." #: ../doc/index.rst:91 msgid ":octicon:`mark-github` Repository" msgstr ":octicon:`mark-github` Repositorio" #: ../doc/index.rst:93 msgid "GitHub repository for the development of TOML Fortran." msgstr "Repositorio Github para el desarrollo de TOML Fortran." #: ../doc/index.rst:102 msgid ":octicon:`link` External" msgstr ":octicon:`link` Externos" #: ../doc/index.rst:104 msgid "External links to other resources, blogs, etc." msgstr "Enlaces externos a otros recursos, blogs, etc." #: ../doc/index.rst:106 msgid "" "TOML Fortran is used for example in the Fortran package manager (`fpm " "`_), a typical package manifest specified " "in TOML is shown below" msgstr "" "TOML Fortran es usado por ejemplo en el Administrador de paquetes de Fortran (`fpm " "`_). Un manifiesto típico para un paquete especificado en " "TOML es mostrado abajo" #: ../doc/index.rst:108 msgid "fpm.toml" msgstr "" fortran-toml-0.5.0/doc/locales/es/LC_MESSAGES/how-to.po0000664000175000017500000011116515201541453022471 0ustar alastairalastair# SOME DESCRIPTIVE TITLE. # Copyright (C) 2019-2022, Sebastian Ehlert # This file is distributed under the same license as the toml-f package. # FIRST AUTHOR , 2022. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: toml-f 0.2.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-06-19 18:08-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Asdrubal Lozada-Blanco\n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.10.3\n" #: ../doc/how-to/array.rst:2 msgid "Working with arrays" msgstr "Trabajando con matrices" #: ../doc/how-to/array.rst:4 msgid "" "TOML supports *array* data types: flat, ordered containers holding " "multiple values. Arrays are dynamically sized (*i.e.* do not need specify" " the number of elements before using an array) and can contain elements " "of any type supported by TOML." msgstr "" "TOML admite tipos de datos *array*: planos, contenedores ordenados " "que contienen valores múltiples. Las matrices son dinámicamente ajustadas (es decir, " " no es necesário especificar la cantidad de elementos antes de usar una matriz) y " "pueden contener elementos de cualquier tipo admitido por TOML." #: ../doc/how-to/array.rst:7 msgid "" "The following program parses and stores an array of integers then prints " "the values to stdout:" msgstr "" "El siguiente programa analiza y almacena una matriz de enteros e imprime " "los valores en stdout:" #: ../doc/how-to/array.rst:9 msgid "app/parse_array.f90" msgstr "" #: ../doc/how-to/array.rst:14 msgid "" "The simplest way to parse an array-valued key in TOML Fortran is to use " "the ``get_value`` interface, which has an overload to handle arrays. " "First, we need to use a temporary variable ``arr`` (of type " "``toml_array``), as the array's elements can be of potentially any type. " "``arr`` is automatically associated and allocated by ``get_value``, which" " we can use to determine how much space is needed to store the array's " "contents. Finally, we must iterate over all elements of the temporary " "``toml_array`` and assign them to an element in our final data structure " "``arr_data`` (which is a standard Fortran array)." msgstr "" #: ../doc/how-to/array.rst:19 msgid "" "As a test, let's run the above program using the following TOML table " "(``array.toml``):" msgstr "" #: ../doc/how-to/array.rst:21 msgid "array.toml" msgstr "" #: ../doc/how-to/array.rst:26 msgid "This produces the following output:" msgstr "" #: ../doc/how-to/array.rst:32 msgid "" "As mentioned above, TOML arrays can contain elements of any type " "supported by TOML. The TOML standard even supports heterogeneous arrays " "containing elements of multiple different types. TOML Fortran supports " "this through the generic ``toml_array`` type." msgstr "" #: ../doc/how-to/array.rst:36 msgid "" "Elements of ``toml_array`` may be of any value but require extra " "processing before they can be used in most Fortran programs, since " "Fortran arrays must only contain elements of a single type known at " "compile time. This is achieved through the generic ``get_value`` " "interface, which automatically coerces elements of the ``toml_array`` to " "the correct type based on the variable it is copied to." msgstr "" #: ../doc/how-to/array.rst:41 msgid "" "The value in ``get_value`` is an ``intent(out)`` argument, in case the " "input and output parameter are incompatible, it is not initialized and " "the actual value is dependent on the compiler settings. By passing an " "integer value to the optional *stat* argument, the procedure will return " "a non-zero value to indicate an error." msgstr "" #: ../doc/how-to/array.rst:46 msgid "Accessing nested arrays" msgstr "" #: ../doc/how-to/array.rst:48 msgid "" "TOML arrays can be nested to produce an *array-of-arrays*. Both the top-" "level and nested child arrays are potentially variable-length and can " "contain any TOML data-type (including further nested arrays)." msgstr "" #: ../doc/how-to/array.rst:50 msgid "" "The following program reads a TOML file with an array-of-arrays nested a " "single level deep, and prints its values to stdout:" msgstr "" #: ../doc/how-to/array.rst:52 msgid "app/nested_array.f90" msgstr "" #: ../doc/how-to/array.rst:56 msgid "" "First, we need to call ``get_value`` to get a pointer to the top-level " "array then iterate through its elements, calling ``get_value`` on each to" " get a pointer to the child array. Finally, we iterate through the " "individual sub-arrays and process their elements one-by-one." msgstr "" #: ../doc/how-to/array.rst:59 msgid "" "This approach has the advantage of being able to deal with nested arrays " "of arbitrary length, such as the TOML table below:" msgstr "" #: ../doc/how-to/array.rst:65 msgid "Which produces the following output:" msgstr "" #: ../doc/how-to/array.rst:78 msgid "" "A TOML array-of-arrays does not necessarily map cleanly to a regular " "Fortran multidimensional array. Multidimensional Fortran arrays must be " "rectangular (*i.e.* rows must contain the same number of elements), so an" " array-of-arrays with variable sized nested arrays cannot be directly " "mapped to a primitive Fortran type and must be represented using a " "compound data type." msgstr "" #: ../doc/how-to/index.rst:4 msgid "How-to guides" msgstr "" #: ../doc/how-to/index.rst:6 msgid "" "This section contains practical guides for working with TOML Fortran. " "Each recipe is tailored for a specific problem and use case it is trying " "to solve." msgstr "" #: ../doc/how-to/index.rst:9 msgid "Contributions are welcome!" msgstr "" #: ../doc/how-to/index.rst:12 msgid "" "If you have written on TOML Fortran and want to contribute to the " "documentation, feel free to fork the repository and submit a pull " "request." msgstr "" #: ../doc/how-to/installation.rst:4 msgid "Installing TOML Fortran" msgstr "" #: ../doc/how-to/installation.rst:6 msgid "" "This guide will walk you through installing the latest version of TOML " "Fortran. If you know your way around fpm, CMake or meson, checkout the " ":ref:`integration guide ` to allow on-demand compilation of " "TOML Fortran as well as discovery of installed libraries." msgstr "" #: ../doc/how-to/installation.rst:11 msgid ":fab:`apple` :fab:`linux` :fab:`windows` Installing from conda-forge" msgstr "" msgid "Conda" msgstr "" #: ../doc/how-to/installation.rst:22 msgid "" "This project is packaged for the *mamba* package manager and available on" " the *conda-forge* channel. To install the *mamba* package manager we " "recommend the `mambaforge `_ installer. If the *conda-forge* channel is " "not yet enabled, add it to your channels with" msgstr "" #: ../doc/how-to/installation.rst:31 msgid "" "Once the *conda-forge* channel has been enabled, TOML Fortran can be " "installed with *mamba*:" msgstr "" #: ../doc/how-to/installation.rst:37 msgid "" "It is possible to list all of the versions of TOML Fortran available on " "your platform with *mamba*:" msgstr "" #: ../doc/how-to/installation.rst:45 msgid ":fab:`freebsd` FreeBSD ports" msgstr "" msgid "FreeBSD" msgstr "" #: ../doc/how-to/installation.rst:51 msgid "A port for FreeBSD is available" msgstr "" #: ../doc/how-to/installation.rst:57 msgid "In case no package is available build the port using" msgstr "" #: ../doc/how-to/installation.rst:64 msgid "" "For more information see the `toml-f port details " "`_." msgstr "" #: ../doc/how-to/installation.rst:68 msgid ":fab:`apple` :fab:`linux` Building with spack" msgstr "" msgid "Spack" msgstr "" #: ../doc/how-to/installation.rst:74 msgid "" "This project is available with the `spack `_ package " "manager. You can install the TOML Fortran package with" msgstr "" #: ../doc/how-to/installation.rst:82 msgid "" "To depend on TOML Fortran in your `spack`_ package you can add a " "dependency with" msgstr "" #: ../doc/how-to/installation.rst:90 msgid ":fab:`linux` Using EasyBuild" msgstr "" #: ../doc/how-to/installation.rst:92 msgid "" "TOML Fortran is available with `EasyBuild " "`_. You can check the " "available configurations using the search option" msgstr "" #: ../doc/how-to/installation.rst:102 msgid "Select one matching configuration and build TOML Fortran using" msgstr "" #: ../doc/how-to/installation.rst:116 msgid "" "TOML Fortran should now be available via an environment module. For more " "details checkout the `EasyBuild`_ documentation." msgstr "" #: ../doc/how-to/installation.rst:121 msgid ":fab:`apple` Using Homebrew" msgstr "" #: ../doc/how-to/installation.rst:123 msgid "" "TOML Fortran is available in a custom tap at `grimme-lab/homebrew-qc " "`__ for the `brew " "`_ package manager. You can include the tap by using" msgstr "" #: ../doc/how-to/installation.rst:131 msgid "" "To build with a custom Fortran compiler you can set the ``FC`` " "environment variable and force to ignore preexisting binary distributions" " from the tap" msgstr "" #: ../doc/how-to/installation.rst:140 msgid "" "TOML Fortran has not yet been submitted to `homebrew-core " "`_." msgstr "" #: ../doc/how-to/installation.rst:144 msgid "Building from source" msgstr "" #: ../doc/how-to/installation.rst:146 msgid "" "To build this project from the source code in this repository you need to" " have" msgstr "" #: ../doc/how-to/installation.rst:148 msgid "a Fortran compiler supporting Fortran 2008" msgstr "" #: ../doc/how-to/installation.rst:150 msgid "GFortran 5 or newer" msgstr "" #: ../doc/how-to/installation.rst:151 msgid "Intel Fortran 18 or newer" msgstr "" #: ../doc/how-to/installation.rst:152 msgid "NAG 7 or newer" msgstr "" #: ../doc/how-to/installation.rst:154 msgid "One of the supported build systems" msgstr "" #: ../doc/how-to/installation.rst:156 msgid "`meson `_ version 0.55 or newer" msgstr "" #: ../doc/how-to/installation.rst:157 msgid "`CMake `_ version 3.9 or newer" msgstr "" #: ../doc/how-to/installation.rst:159 msgid "First, get the source by cloning the repository" msgstr "" #: ../doc/how-to/installation.rst:168 msgid "Using Meson" msgstr "" #: ../doc/how-to/installation.rst:170 msgid "" "To build this project with meson a build-system backend is required, " "*i.e.* `ninja `_ version 1.7 or newer. Setup a " "build with" msgstr "" #: ../doc/how-to/installation.rst:177 msgid "" "You can select the Fortran compiler by the ``FC`` environment variable. " "To compile the project run" msgstr "" #: ../doc/how-to/installation.rst:184 msgid "" "We employ a `validator suite `_ " "to test the standard compliance of this implementation. To use this " "testing a *go* installation is required. The installation of the " "validator suite will be handled by meson automatically without installing" " into the users *go* workspace. Run the tests with" msgstr "" #: ../doc/how-to/installation.rst:193 msgid "" "To run the full decoder test add the benchmark argument. This test will " "currently fail, due to the implementation not yet supporting Unicode " "escape sequences." msgstr "" #: ../doc/how-to/installation.rst:200 msgid "" "The binary used for transcribing the TOML documents to the testing format" " is ``_build/test/toml2json`` and can be used to check on per test basis." " Finally, you can install TOML Fortran with" msgstr "" #: ../doc/how-to/installation.rst:209 msgid "Using CMake" msgstr "" #: ../doc/how-to/installation.rst:211 msgid "" "While meson is the preferred way to build this project it also offers " "CMake support. Configure the CMake build with" msgstr "" #: ../doc/how-to/installation.rst:218 msgid "" "Similar to meson the compiler can be selected with the ``FC`` environment" " variable. You can build the project using" msgstr "" #: ../doc/how-to/installation.rst:225 msgid "" "To include *toml-f* in your CMake project, check the [example integration" " with CMake](https://github.com/toml-f/tf-cmake-example). The validation " "suite is currently not supported as unit test for CMake builds and " "requires a manual setup instead using the *toml2json* binary. Finally, " "you can install TOML Fortran with" msgstr "" #: ../doc/how-to/installation.rst:235 msgid "Supported compilers" msgstr "" #: ../doc/how-to/installation.rst:237 msgid "" "This is a non-comprehensive list of tested compilers for TOML Fortran. " "Compilers with the label *latest* are tested with continuous integration " "for each commit." msgstr "" #: ../doc/how-to/installation.rst:241 ../doc/how-to/installation.rst:260 msgid "Compiler" msgstr "" #: ../doc/how-to/installation.rst:241 ../doc/how-to/installation.rst:260 msgid "Version" msgstr "" #: ../doc/how-to/installation.rst:241 ../doc/how-to/installation.rst:260 msgid "Platform" msgstr "" #: ../doc/how-to/installation.rst:241 ../doc/how-to/installation.rst:260 msgid "Architecture" msgstr "" #: ../doc/how-to/installation.rst:241 msgid "version" msgstr "" #: ../doc/how-to/installation.rst:243 ../doc/how-to/installation.rst:244 #: ../doc/how-to/installation.rst:245 ../doc/how-to/installation.rst:246 #: ../doc/how-to/installation.rst:247 msgid "GCC" msgstr "" #: ../doc/how-to/installation.rst:243 msgid "11.1, 10.3, 9.4, 8.5, 7.5" msgstr "" #: ../doc/how-to/installation.rst:243 ../doc/how-to/installation.rst:250 #: ../doc/how-to/installation.rst:262 msgid "Ubuntu 20.04" msgstr "" #: ../doc/how-to/installation.rst:243 ../doc/how-to/installation.rst:244 #: ../doc/how-to/installation.rst:248 ../doc/how-to/installation.rst:249 #: ../doc/how-to/installation.rst:250 ../doc/how-to/installation.rst:251 #: ../doc/how-to/installation.rst:252 ../doc/how-to/installation.rst:262 #: ../doc/how-to/installation.rst:263 msgid "x86_64" msgstr "" #: ../doc/how-to/installation.rst:243 ../doc/how-to/installation.rst:244 #: ../doc/how-to/installation.rst:248 ../doc/how-to/installation.rst:250 msgid "0.2.3, latest" msgstr "" #: ../doc/how-to/installation.rst:244 msgid "9.4, 6.5" msgstr "" #: ../doc/how-to/installation.rst:244 msgid "MacOS 10.15.7" msgstr "" #: ../doc/how-to/installation.rst:245 msgid "11.0" msgstr "" #: ../doc/how-to/installation.rst:245 msgid "MacOS 11.0" msgstr "" #: ../doc/how-to/installation.rst:245 msgid "arm64" msgstr "" #: ../doc/how-to/installation.rst:245 ../doc/how-to/installation.rst:246 #: ../doc/how-to/installation.rst:247 ../doc/how-to/installation.rst:249 #: ../doc/how-to/installation.rst:251 ../doc/how-to/installation.rst:252 msgid "0.2.3" msgstr "" #: ../doc/how-to/installation.rst:246 ../doc/how-to/installation.rst:247 msgid "9.4" msgstr "" #: ../doc/how-to/installation.rst:246 ../doc/how-to/installation.rst:247 msgid "CentOS 7" msgstr "" #: ../doc/how-to/installation.rst:246 msgid "ppc64le" msgstr "" #: ../doc/how-to/installation.rst:247 msgid "aarch64" msgstr "" #: ../doc/how-to/installation.rst:248 ../doc/how-to/installation.rst:249 msgid "GCC/MinGW" msgstr "" #: ../doc/how-to/installation.rst:248 msgid "8.1" msgstr "" #: ../doc/how-to/installation.rst:248 ../doc/how-to/installation.rst:249 msgid "Window Server 2019" msgstr "" #: ../doc/how-to/installation.rst:249 msgid "5.3" msgstr "" #: ../doc/how-to/installation.rst:250 ../doc/how-to/installation.rst:251 msgid "Intel" msgstr "" #: ../doc/how-to/installation.rst:250 msgid "2022.0" msgstr "" #: ../doc/how-to/installation.rst:251 msgid "19" msgstr "" #: ../doc/how-to/installation.rst:251 msgid "OpenSUSE" msgstr "" #: ../doc/how-to/installation.rst:252 msgid "NAG" msgstr "" #: ../doc/how-to/installation.rst:252 msgid "7.1" msgstr "" #: ../doc/how-to/installation.rst:252 msgid "RHEL" msgstr "" #: ../doc/how-to/installation.rst:255 msgid "" "Compiler known to fail are documented here, together with the last commit" " where this behaviour was encountered. If available an issue in on the " "projects issue tracker or the issue tracker of the dependencies is " "linked. Usually, it safe to assume that older versions of the same " "compiler will fail to compile as well and this failure is consistent over" " platforms and/or architectures." msgstr "" #: ../doc/how-to/installation.rst:260 msgid "Reference" msgstr "" #: ../doc/how-to/installation.rst:262 msgid "Flang" msgstr "" #: ../doc/how-to/installation.rst:262 msgid "20190329" msgstr "" #: ../doc/how-to/installation.rst:262 msgid "`f066ec6`_, `toml-f#28`_" msgstr "" #: ../doc/how-to/installation.rst:263 msgid "NVHPC" msgstr "" #: ../doc/how-to/installation.rst:263 msgid "20.9" msgstr "" #: ../doc/how-to/installation.rst:263 msgid "Manjaro Linux" msgstr "" #: ../doc/how-to/installation.rst:263 msgid "`f066ec6`_, `toml-f#27`_" msgstr "" #: ../doc/how-to/integration.rst:4 msgid "Using TOML Fortran" msgstr "" #: ../doc/how-to/integration.rst:6 msgid "" "This tutorial shows how to integrate the TOML Fortran library with your " "build system and use it easily in your project." msgstr "" #: ../doc/how-to/integration.rst:10 msgid "Using the Fortran package manager" msgstr "" #: ../doc/how-to/integration.rst:12 msgid "" "The Fortran package manager (`fpm `_) is a " "tool for building Fortran projects and managing dependencies on Fortran " "libraries. To enable TOML Fortran in your fpm project add the following " "entry to your package manifest:" msgstr "" #: ../doc/how-to/integration.rst:15 msgid "fpm.toml" msgstr "" #: ../doc/how-to/integration.rst:21 msgid "" "When building your project fpm will automatically fetch TOML Fortran for " "you and build it as part of your project. The TOML Fortran modules become" " useable in your project." msgstr "" #: ../doc/how-to/integration.rst:26 msgid "Integrate with meson" msgstr "" #: ../doc/how-to/integration.rst:28 msgid "" "To allow meson to use TOML Fortran it is easiest to include it as " "subproject using a git wrap file placed in the ``subprojects`` directory." msgstr "" #: ../doc/how-to/integration.rst:30 msgid "subprojects/toml-f.wrap" msgstr "" #: ../doc/how-to/integration.rst:38 msgid "" "The revision can be adjusted to pin a specific release tag of TOML " "Fortran for additional stability. In the projects meson buid file the " "dependency method can be used to access TOML Fortran using the wrap file " "to define a fallback which is built on-demand." msgstr "" #: ../doc/how-to/integration.rst:41 msgid "meson.build" msgstr "" #: ../doc/how-to/integration.rst:52 msgid "" "Finally, you can add ``tomlf_dep`` as dependency to any of your targets " "and are done." msgstr "" #: ../doc/how-to/integration.rst:56 msgid "Integrate with CMake" msgstr "" #: ../doc/how-to/integration.rst:58 msgid "" "To use TOML Fortran in CMake based projects it is useful to define your " "own find-module, to allow on-demand compilation of TOML Fortran as well " "as discovery of installed packages from both meson and CMake based " "builds." msgstr "" #: ../doc/how-to/integration.rst:68 msgid "" "In your main CMake build file you have to include the custom find-module " "in your ``CMAKE_MODULE_PATH``, afterwards you can just use " "``find_package`` to obtain the ``toml-f::toml-f`` target and link against" " it." msgstr "" #: ../doc/how-to/integration.rst:70 msgid "CMakeLists.txt" msgstr "" #: ../doc/how-to/integration.rst:111 msgid "" "Note that we also install the find-modules, this is important if you want" " to make your CMake projects reusable in the same way TOML Fortran can be" " used in your project. Finally we need some boilerplate to define the " "custom find-module is documented below." msgstr "" #: ../doc/how-to/integration.rst:115 msgid "Imported Targets" msgstr "" #: ../doc/how-to/integration.rst:117 msgid "This module provides the following imported target, if found:" msgstr "" #: ../doc/how-to/integration.rst:121 msgid "``toml-f::toml-f``" msgstr "" #: ../doc/how-to/integration.rst:120 msgid "The toml-f library" msgstr "" #: ../doc/how-to/integration.rst:124 msgid "Result Variables" msgstr "" #: ../doc/how-to/integration.rst:126 msgid "This module will define the following variables:" msgstr "" #: ../doc/how-to/integration.rst:129 msgid "``TOML_FORTRAN_FOUND``" msgstr "" #: ../doc/how-to/integration.rst:129 msgid "True if the toml-f library is available" msgstr "" #: ../doc/how-to/integration.rst:133 msgid "``TOML_FORTRAN_SOURCE_DIR``" msgstr "" #: ../doc/how-to/integration.rst:132 msgid "" "Path to the source directory of the toml-f project, only set if the " "project is included as source." msgstr "" #: ../doc/how-to/integration.rst:137 msgid "``TOML_FORTRAN_BINARY_DIR``" msgstr "" #: ../doc/how-to/integration.rst:136 msgid "" "Path to the binary directory of the toml-f project, only set if the " "project is included as source." msgstr "" #: ../doc/how-to/integration.rst:140 msgid "Cache variables" msgstr "" #: ../doc/how-to/integration.rst:142 msgid "" "The following cache variables may be set to influence the library " "detection:" msgstr "" #: ../doc/how-to/integration.rst:150 msgid "``TOML_FORTRAN_FIND_METHOD``" msgstr "" #: ../doc/how-to/integration.rst:145 msgid "Methods to find or make the project available. Available methods are" msgstr "" #: ../doc/how-to/integration.rst:147 msgid "``cmake``: Try to find via CMake config file" msgstr "" #: ../doc/how-to/integration.rst:148 msgid "``pkgconf``: Try to find via pkg-config file" msgstr "" #: ../doc/how-to/integration.rst:149 msgid "``subproject``: Use source in subprojects directory" msgstr "" #: ../doc/how-to/integration.rst:150 msgid "``fetch``: Fetch the source from upstream" msgstr "" #: ../doc/how-to/integration.rst:153 msgid "``TOML_FORTRAN_DIR``" msgstr "" #: ../doc/how-to/integration.rst:153 msgid "Used for searching the CMake config file" msgstr "" #: ../doc/how-to/integration.rst:156 msgid "``TOML_FORTRAN_SUBPROJECT``" msgstr "" #: ../doc/how-to/integration.rst:156 msgid "Directory to find the toml-f subproject, relative to the project root" msgstr "" #: ../doc/how-to/integration.rst:158 msgid "cmake/Findtoml-f.cmake" msgstr "" #: ../doc/how-to/integration.rst:280 msgid "Other build systems" msgstr "" #: ../doc/how-to/integration.rst:282 msgid "" "Other build systems must discover a precompiled TOML Fortran library from" " the system. For this purpose the ``pkg-config`` tool is used. After " "installing TOML Fortran with either meson or CMake a pc-file is generated" " which can be discovered by ``pkg-config`` and describes how to compile " "against the installed module files as well as link against the TOML " "Fortran library. First check if the ``pkg-config`` tool is available and " "can discover TOML Fortran" msgstr "" #: ../doc/how-to/integration.rst:291 msgid "" "Make sure to adjust the ``PKG_CONFIG_PATH`` environment variable to point" " to the correct installation directory. Using the ``--libs`` and " "``--cflags`` options the libraries to link against as well as the include" " directories can be obtained:" msgstr "" #: ../doc/how-to/integration.rst:299 msgid "In a handwritten Makefile those can be included by" msgstr "" #: ../doc/how-to/serde.rst:2 msgid "Serializable base class" msgstr "" #: ../doc/how-to/serde.rst:4 msgid "" "This recipe shows how to create a serializable class based on TOML " "Fortran. Currently, TOML Fortran does not define such a base class " "itself, therefore we define a loader and dumper interface for turning a " "file or connected unit into a data structure. The abstract base class " "will implement the processing of the file or unit to a TOML data " "structure and pass it to a deferred procedure which the implementing " "class uses to define its mapping from and back to the TOML data " "structure. This way an easily round-tripable data structure can be " "created and used in a variety of contexts." msgstr "" #: ../doc/how-to/serde.rst:11 msgid "" "TOML Fortran might provide such abstract base class in the future " "natively." msgstr "" #: ../doc/how-to/serde.rst:13 msgid "The base class can be defined as" msgstr "" #: ../doc/how-to/serde.rst:15 msgid "src/serde_class.f90" msgstr "" #: ../doc/how-to/serde.rst:19 msgid "" "We also define a convenience error handler which holds the error message " "and signals its error status by its allocation state." msgstr "" #: ../doc/how-to/serde.rst:21 msgid "src/serde_error.f90" msgstr "" #: ../doc/how-to/serde.rst:25 msgid "" "An example for a serializable class based on the above base class is " "given below." msgstr "" #: ../doc/how-to/serde.rst:27 msgid "src/demo.f90" msgstr "" #: ../doc/how-to/serde.rst:31 msgid "" "The defined data class can in an application easily be loaded from a " "file, while the actual implementation does not have to deal with getting " "the TOML data structure from the file but can assume that if the " "configuration file was valid TOML it will be provided with a data " "structure to read from." msgstr "" #: ../doc/how-to/table.rst:2 msgid "Working with tables" msgstr "" #: ../doc/how-to/table.rst:4 msgid "" "The central data structures in TOML are tables, they contain a map from a" " key (string) to any supported data type in TOML. These recipes describe " "common scenarios for retrieving data from tables using the TOML Fortran " "library." msgstr "" #: ../doc/how-to/table.rst:9 msgid "Accessing nested tables" msgstr "" #: ../doc/how-to/table.rst:11 msgid "" "Using nested tables provides the possibility to better group " "configuration data. Since the TOML format always requires the full " "qualified path in each table header, it is easy for the user to identify " "where the current settings belong to. On the other hand, deeply nested " "tables with long table paths or path components make them more difficult " "to use and a good balance of short and expressive table names and " "meaningful subtables is required." msgstr "" #: ../doc/how-to/table.rst:15 msgid "" "An example of an electronic structure code implementing different " "Hamiltonians is given below." msgstr "" #: ../doc/how-to/table.rst:26 msgid "" "The deepest nested subtable with entries in this example is the " "*hamiltonian.dftb.skf* path." msgstr "" #: ../doc/how-to/table.rst:28 msgid "" "Such layout in the configuration file will usually be mirrored in the " "actual implementation, with every table corresponding to a derived type " "describing the input. For the example above in total six derived types " "for the individual tables are defined as" msgstr "" #: ../doc/how-to/table.rst:31 ../doc/how-to/table.rst:51 #: ../doc/how-to/table.rst:58 ../doc/how-to/table.rst:67 #: ../doc/how-to/table.rst:84 msgid "src/input.f90" msgstr "" #: ../doc/how-to/table.rst:38 #, python-format msgid "" "The representation in Fortran derived types looks lengthy compared to the" " actual TOML input. Consider that the 40 lines of Fortran code contain " "50% comments describing the data types briefly for (future) developers. " "Of course, the user documentation of the input format will be much more " "extensive, containing descriptions for every table and every entry, " "including input ranges and unit conventions. The final input file " "provided by the user can be brief and expressive." msgstr "" #: ../doc/how-to/table.rst:43 msgid "" "Staring with the root of the table which is read in the " "*simulation_input* there are two ways to obtain access to a subtable, " "first we get the *hamiltonian* subtable, which we defined as mandatory, " "using the ``get_value`` interface. In case it is present a reference will" " be returned in the *child* pointer. If no table is available in the " "input TOML Fortran will insert it into the root table and return the " "reference to the newly created table. The *child* pointer can still be " "unassigned in case invalid input is provided, which will result in " "raising an error in the implementation shown below." msgstr "" #: ../doc/how-to/table.rst:48 msgid "" "The alternative is to explicitly mark the subtable as optional, like for " "the *analysis* table, if no table is available or the entry is invalid " "the *child* pointer will not be assigned. To differentiate those cases we" " can request the status information, check whether the operation was " "successful, and cleanly handle the error case." msgstr "" #: ../doc/how-to/table.rst:56 msgid "" "The same happens for reading the *hamiltonian_input* and *dftb_input* " "entry." msgstr "" #: ../doc/how-to/table.rst:63 msgid "" "Finally, we can implement reading the terminal subtables into the " "*scc_input*, *skf_input*, and *analysis_input*, where we retrieve the " "actual values using the ``get_value`` interface. Note that we can " "conveniently define default values using the ``get_value`` interface. For" " proper error handling, we can retrieve the optional *stat* argument as " "well." msgstr "" #: ../doc/how-to/table.rst:72 msgid "" "For the small incomplete input as shown here, the fine-grained " "substructure seems overengineered and could be fully defined in the " "reading routine for the document root as well. However, for larger " "program inputs such a structure can help to ensure that input readers are" " properly modular and reusable." msgstr "" #: ../doc/how-to/table.rst:77 msgid "" "The allocation status of a component of the derived type can be used " "instead of a separate boolean flag to indicate whether a feature should " "be activated. This avoids requiring conditional code inside a reader " "routine for conditionally handling entries depending on a boolean flag, " "instead they can be collected in a subtable." msgstr "" #: ../doc/how-to/table.rst msgid "Full source code" msgstr "" #: ../doc/how-to/table.rst:82 msgid "The full module implementing the *simulation_input* reading" msgstr "" #: ../doc/how-to/table.rst:88 ../doc/how-to/table.rst:161 msgid "The auxiliary module providing the error handler" msgstr "" #: ../doc/how-to/table.rst:90 ../doc/how-to/table.rst:163 msgid "src/error.f90" msgstr "" #: ../doc/how-to/table.rst:96 msgid "Iterating over keys" msgstr "" #: ../doc/how-to/table.rst:98 msgid "" "An expressive way to organize data is by providing a table where the keys" " of each entry describe the object that should be initialized. For " "example in a package manager, the keys represent the dependency, where " "each dependency is declared in a subtable. Furthermore, a convenience " "feature might be the possibility to just provide a string, which is " "interpreted as a version subentry." msgstr "" #: ../doc/how-to/table.rst:102 msgid "" "The final usage of this in a *requirements* table could look like the " "snippet shown below." msgstr "" #: ../doc/how-to/table.rst:114 msgid "" "The first three entries provide a string value, while the fourth entry " "provides a subtable implicitly by using dotted key-value pairs and the " "last entry uses an inline table." msgstr "" #: ../doc/how-to/table.rst:116 msgid "" "Here we want to focus on the iteration and the default initialization, " "the internal structure of the *requirement_type* is secondary for this " "example. We provide the minimal definition only holding the name of the " "dependency for demonstration purposes." msgstr "" #: ../doc/how-to/table.rst:119 ../doc/how-to/table.rst:129 #: ../doc/how-to/table.rst:144 ../doc/how-to/table.rst:157 msgid "src/requirements.f90" msgstr "" #: ../doc/how-to/table.rst:124 msgid "" "For the actual implementation of reading all entries from the table, we " "will use a one-dimensional array of *requirement_type* values. Using the " "``get_keys`` method of the table we can obtain a list of all keys for the" " current table, the method will always allocate the ``list`` variable and" " we can safely allocate the *requirement_type* using the number of keys. " "To obtain the subtable, the ``get_value`` interface can be used, it will " "return a pointer to the subtable, either created implicitly by using a " "dotted key-value pair or by an inline table as shown in the snippet " "above. Finally, we can call the actual constructor of the " "*requirement_type* using the subtable references with the ``child`` " "pointer." msgstr "" #: ../doc/how-to/table.rst:134 msgid "" "The other scenario we want to support is the presence of a string rather " "than a subtable. In this case, the ``get_value`` interface will fail, " "while it provides an optional status argument to check for successful " "operation, we can more conveniently and idiomatically verify the success " "by checking the state of the ``child`` pointer. If there is no subtable " "to reference, *i.e.* because it is a key-value pair with a string entry, " "the ``child`` pointer will not be associated, which can be easily " "checked. For this case we will again use the ``get_value`` interface, but" " this time to retrieve the entry into a deferred length character. Again " "we can idiomatically check the status of the operation using the " "allocation state of the variable and create the appropriate error message" " if needed. Eventually, we have to provide the constructor of the " "requirements with a table, for this purpose we create a dummy table and " "set the entry at the version key to the just retrieved string. The newly " "created dummy table can be associated with the ``child`` pointer and " "passed to the actual constructor." msgstr "" #: ../doc/how-to/table.rst:142 msgid "" "The actual constructor for our example is very minimalistic and only " "recovers the name of the dependency which is passed as a separate " "argument." msgstr "" #: ../doc/how-to/table.rst:151 msgid "" "While we provide an error handler in the example, we also ensure that the" " allocation status of the *requirement_type* values communicates the " "status of the operation as well." msgstr "" #: ../doc/how-to/table.rst:155 msgid "The full module implementing the *requirement_type* reading" msgstr "" #: ../doc/how-to/table.rst:169 msgid "Array of tables" msgstr "" #: ../doc/how-to/table.rst:171 msgid "" "A special construct in TOML is the array of tables syntax, it provides a " "more verbose form to declare several tables in an array, which are " "usually provided using inline tables as shown below." msgstr "" #: ../doc/how-to/table.rst:182 msgid "" "Comparing the above example to the snippet below using an array of tables" " for the *tasks* array, the more verbose form becomes preferable in case " "further subtables are needed. Except for the subtables *config* the same " "data is provided." msgstr "" #: ../doc/how-to/table.rst:210 msgid "" "To represent this data we can use a single *task_config* derived type " "with a polymorphic *driver_config* member identifying the actual task. " "For this example, we will have two implementations of such tasks such as " "LBFGS and Velocity Verlet, which are defined in the following snippets." msgstr "" #: ../doc/how-to/table.rst:213 ../doc/how-to/table.rst:222 #: ../doc/how-to/table.rst:236 ../doc/how-to/table.rst:244 #: ../doc/how-to/table.rst:255 msgid "src/task.f90" msgstr "" #: ../doc/how-to/table.rst:218 msgid "" "To read the array of tables we start from the root document and fetch the" " *tasks* entry as an array using the ``get_value`` interface. The length " "of the full arrays is known and we can use it to allocate the list of " "*task_config* values before reading the individual entries. The " "individual tables inside the array can be addressed using the " "``get_value`` interface by passing the (one-based) index." msgstr "" #: ../doc/how-to/table.rst:229 msgid "" "In the setup above, if the *tasks* entry is not present it will be " "implicitly created as an empty array. The allocation and the loop over " "the entries will work, however the consuming code should check whether no" " tasks are meaningful or should produce an error." msgstr "" #: ../doc/how-to/table.rst:232 msgid "" "To read the individual tasks we define a separate procedure to make it " "easily reusable and hide the fact that we are working with a subtable. To" " make the task *name* optional we make it default to the driver name, for" " *allocatable* or *pointer* variables the exit status of ``get_value`` " "can be easily checked by the allocation or association status of the " "respective variable, alternatively an integer variable can be passed to " "the optional *stat* argument. Finally, the configuration reader is called" " depending on the value of *driver* for ease of usage we use a block " "construct to allocate the specific type and then transfer it using " "*move_alloc* into the *task_config*." msgstr "" #: ../doc/how-to/table.rst:241 msgid "" "For reading the actual driver configuration we use the ``get_value`` " "interface to obtain the settings. We use the same defaulting mechanism as" " for the *name* entry here." msgstr "" #: ../doc/how-to/table.rst:249 msgid "" "Note that this example does not propagate back errors but directly calls " "*error stop*, for a more robust error reporting this can be changed by a " "small error handle or a context type." msgstr "" #: ../doc/how-to/table.rst:253 msgid "The full module implementing the *task_config* reading" msgstr "" fortran-toml-0.5.0/doc/locales/es/LC_MESSAGES/external.po0000664000175000017500000000221115201541453023065 0ustar alastairalastair# SOME DESCRIPTIVE TITLE. # Copyright (C) 2019-2022, Sebastian Ehlert # This file is distributed under the same license as the toml-f package. # FIRST AUTHOR , 2022. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: toml-f 0.2.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-06-19 18:08-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Asdrubal Lozada-Blanco\n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.10.3\n" #: ../doc/external.rst:9 msgid "Modern Fortran by Philipp Engel (English)" msgstr "Modern Fortran por Philipp Engel (Inglés)" #: ../doc/external.rst:9 msgid "Fortran in Action by Zuo Zhihua (Chinese)" msgstr "Fortran in Action por Zuo Zhihua (Chino)" #: ../doc/external.rst:4 msgid "External resources" msgstr "Recursos externos" #: ../doc/external.rst:6 msgid "" "Sides and blog posts with information on TOML Fortran are mentioned and " "linked here." msgstr "" "Páginas y publicaciones en blogs con información sobre TOML Fortran son mencionadas y " "enlzazadas aquí." fortran-toml-0.5.0/doc/tutorial/0000775000175000017500000000000015201541453016734 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/json/0000775000175000017500000000000015201541453017705 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/json/test/0000775000175000017500000000000015201541453020664 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/json/test/main.f900000664000175000017500000000437415201541453022140 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Wrapper for the testsuites program tester use, intrinsic :: iso_fortran_env, only : error_unit use testdrive, only : run_testsuite, new_testsuite, testsuite_type, & & select_suite, run_selected, get_argument use test_lexer, only : collect_lexer implicit none integer :: stat, is character(len=:), allocatable :: suite_name, test_name type(testsuite_type), allocatable :: testsuites(:) character(len=*), parameter :: fmt = '("#", *(1x, a))' stat = 0 testsuites = [ & & new_testsuite("lexer", collect_lexer) & & ] call get_argument(1, suite_name) call get_argument(2, test_name) if (allocated(suite_name)) then is = select_suite(testsuites, suite_name) if (is > 0 .and. is <= size(testsuites)) then if (allocated(test_name)) then write(error_unit, fmt) "Suite:", testsuites(is)%name call run_selected(testsuites(is)%collect, test_name, error_unit, stat) if (stat < 0) then error stop 1 end if else write(error_unit, fmt) "Testing:", testsuites(is)%name call run_testsuite(testsuites(is)%collect, error_unit, stat) end if else write(error_unit, fmt) "Available testsuites" do is = 1, size(testsuites) write(error_unit, fmt) "-", testsuites(is)%name end do error stop 1 end if else do is = 1, size(testsuites) write(error_unit, fmt) "Testing:", testsuites(is)%name call run_testsuite(testsuites(is)%collect, error_unit, stat) end do end if if (stat > 0) then write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" error stop 1 end if end program tester fortran-toml-0.5.0/doc/tutorial/json/test/test_lexer.f900000664000175000017500000003515515201541453023373 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module test_lexer use testdrive use tjson_lexer use tomlf_constants, only : tfi, tfr, nl => TOML_NEWLINE implicit none public :: collect_lexer contains !> Collect all exported unit tests subroutine collect_lexer(testsuite) !> Collection of tests type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & & new_unittest("bool-true", bool_true), & & new_unittest("bool-true-or-not", bool_nottrue), & & new_unittest("bool-false", bool_false), & & new_unittest("bool-falsey", bool_falsey), & & new_unittest("brace", brace), & & new_unittest("brace-right", brace_right), & & new_unittest("bracket", bracket), & & new_unittest("bracket-left", bracket_left), & & new_unittest("bracket-right", bracket_right), & & new_unittest("colon", colon), & & new_unittest("empty", empty), & & new_unittest("equal", equal), & & new_unittest("float-point", float_point), & & new_unittest("float-exponent", float_exponent), & & new_unittest("float-zero", float_zero), & & new_unittest("float-double-point", float_double_point), & & new_unittest("float-double-expo", float_double_expo), & & new_unittest("float-invalid", float_invalid), & & new_unittest("integer-limits", integer_limits), & & new_unittest("integer-leading-zero", integer_leading_zero), & & new_unittest("string", string), & & new_unittest("string-unclosed", string_unclosed), & & new_unittest("string-control", string_control), & & new_unittest("string-escape", string_escape), & & new_unittest("token-integer", token_integer), & & new_unittest("token-float", token_float), & & new_unittest("token-bool", token_bool), & & new_unittest("whitespace-blank", whitespace_blank), & & new_unittest("whitespace-tab", whitespace_tab), & & new_unittest("whitespace-mixed", whitespace_mixed)] end subroutine collect_lexer subroutine empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "", & & [token_kind%eof]) end subroutine empty subroutine brace(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "{}", & & [token_kind%lbrace, token_kind%rbrace, token_kind%eof]) end subroutine brace subroutine brace_right(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "}", & & [token_kind%rbrace, token_kind%eof]) end subroutine brace_right subroutine bracket(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "[]", & & [token_kind%lbracket, token_kind%rbracket, token_kind%eof]) end subroutine bracket subroutine bracket_left(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "[", & & [token_kind%lbracket, token_kind%eof]) end subroutine bracket_left subroutine bracket_right(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "]", & & [token_kind%rbracket, token_kind%eof]) end subroutine bracket_right subroutine dot_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, ".", & & [token_kind%invalid, token_kind%eof]) end subroutine dot_invalid subroutine comma(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, ",", & & [token_kind%comma, token_kind%eof]) end subroutine comma subroutine colon(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, ":", & & [token_kind%equal, token_kind%eof]) end subroutine colon subroutine equal(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "=", & & [token_kind%invalid, token_kind%eof]) end subroutine equal subroutine string(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """something"",""anything""", & & [token_kind%string, token_kind%comma, token_kind%string, token_kind%eof]) end subroutine string subroutine string_unclosed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """right open"//nl//"""fully closed""", & & [token_kind%invalid, token_kind%whitespace, token_kind%string, token_kind%eof]) end subroutine string_unclosed subroutine string_control(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """control char "//achar(0)//""",""normal string""", & & [token_kind%invalid, token_kind%comma, token_kind%string, token_kind%eof]) end subroutine string_control subroutine string_escape(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """a \b backspace character"",""a \t tab character"""//nl//& & """a \n new line character"",""a \f form feed character"""//nl//& & """a \r carriage return character"",""a \"" quote character"""//nl//& & """a \\ backslash character"""//nl//& & """not a unicode \\u escape"",""not a unicode \u005Cu escape"""//nl//& & """not a unicode \\u0075 escape"",""not a unicode \\\u0075 escape"""//nl//& & """a \u007F delete control code"",""a \u001F unit separator control code""", & & [token_kind%string, token_kind%comma, token_kind%string, token_kind%whitespace, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%whitespace, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%whitespace, & & token_kind%string, token_kind%whitespace, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%whitespace, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%whitespace, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%eof]) end subroutine string_escape subroutine whitespace_blank(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, " ", & & [token_kind%whitespace, token_kind%eof]) end subroutine whitespace_blank subroutine whitespace_tab(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, achar(9)//achar(9), & & [token_kind%whitespace, token_kind%eof]) end subroutine whitespace_tab subroutine whitespace_mixed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, achar(9)//" "//achar(9), & & [token_kind%whitespace, token_kind%eof]) end subroutine whitespace_mixed subroutine bool_false(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "false", & & [token_kind%bool, token_kind%eof]) end subroutine bool_false subroutine bool_falsey(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "falsey", & & [token_kind%invalid, token_kind%eof]) end subroutine bool_falsey subroutine bool_true(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "true", & & [token_kind%bool, token_kind%eof]) end subroutine bool_true subroutine bool_nottrue(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "true-or-not", & & [token_kind%invalid, token_kind%eof]) end subroutine bool_nottrue subroutine float_point(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "3.14,+3.14,-3.14,0.123", & & [token_kind%float, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%eof]) end subroutine float_point subroutine float_exponent(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "3e2,3E2,3e-2,3E+2,3e0,3.1e2,3.1E2,-1E-1", & & [token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%eof]) end subroutine float_exponent subroutine float_zero(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "0.0,+0.0,-0.0,0e0,0e00,+0e0,-0e0", & & [token_kind%float, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%float, token_kind%eof]) end subroutine float_zero subroutine float_double_point(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "0..1,0.1.2", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%eof]) end subroutine float_double_point subroutine float_double_expo(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1ee2,1e2e3", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%eof]) end subroutine float_double_expo subroutine float_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "+1.2,1.,+1.,-1.,03.14,+03.14,-03.14", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof]) end subroutine float_invalid subroutine integer_limits(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "9223372036854775807,-9223372036854775808", & & [token_kind%int, token_kind%comma, token_kind%int, token_kind%eof]) end subroutine integer_limits subroutine integer_leading_zero(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "001", & & [token_kind%invalid, token_kind%eof]) end subroutine integer_leading_zero subroutine token_integer(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(json_lexer) :: lexer integer(tfi) :: val1 call new_lexer_from_string(lexer, "1023,2047,-612") call lexer%extract(toml_token(token_kind%int, 1, 4), val1) call check(error, val1, 1023_tfi) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 6, 9), val1) call check(error, val1, 2047_tfi) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 11, 14), val1) call check(error, val1, -612_tfi) if (allocated(error)) return end subroutine token_integer subroutine token_float(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(json_lexer) :: lexer real(tfr) :: val1 call new_lexer_from_string(lexer, "1023.0,20000.47,-6.12e-1,1e0,1e+0,1e-0") call lexer%extract(toml_token(token_kind%float, 1, 6), val1) call check(error, val1, 1023.0_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 8, 15), val1) call check(error, val1, 20000.47_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 17, 24), val1) call check(error, val1, -6.12e-1_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 26, 28), val1) call check(error, val1, 1.0_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 30, 33), val1) call check(error, val1, 1.0_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 35, 38), val1) call check(error, val1, 1.0_tfr) if (allocated(error)) return end subroutine token_float subroutine token_bool(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(json_lexer) :: lexer logical :: val call new_lexer_from_string(lexer, "true,false") call lexer%extract(toml_token(token_kind%bool, 1, 4), val) call check(error, val .eqv. .true.) if (allocated(error)) return call lexer%extract(toml_token(token_kind%bool, 6, 10), val) call check(error, val .eqv. .false.) if (allocated(error)) return end subroutine token_bool subroutine check_token(error, string, expected) use tomlf_diagnostic, only : render, toml_label, toml_level use tomlf_de_token, only : stringify use tomlf_terminal, only : toml_terminal !> Error handling type(error_type), allocatable, intent(out) :: error !> String to be parsed character(len=*), intent(in) :: string !> Expected token kind integer, intent(in) :: expected(:) integer :: it logical :: okay character(len=:), allocatable :: msg type(json_lexer) :: lexer type(toml_token) :: token type(toml_label), allocatable :: label(:) call new_lexer_from_string(lexer, string) lexer%prelude = 0 allocate(label(0)) do it = 1, size(expected) call lexer%next(token) okay = token%kind == expected(it) label = [label, toml_label(merge(toml_level%info, toml_level%error, okay), & & token%first, token%last, stringify(token), .not.okay)] msg = render(string//nl, [label(size(label))], toml_terminal(.true.)) call check(error, token%kind, expected(it), & & "Expected '"//stringify(toml_token(expected(it)))// & & "' but got '"//stringify(token)//"'"//nl//msg) if (allocated(error)) exit end do if (.not.allocated(error)) then msg = render(string//nl, label, toml_terminal(.true.)) print '(a)', msg end if end subroutine check_token end module test_lexer fortran-toml-0.5.0/doc/tutorial/json/fpm.toml0000664000175000017500000000027515201541453021370 0ustar alastairalastairname = "tjson" [library] source-dir = "../../../test/compliance" [dependencies] toml-f.path = "../../.." [dev-dependencies] test-drive.git = "https://github.com/fortran-lang/test-drive" fortran-toml-0.5.0/doc/tutorial/fpm-lint/0000775000175000017500000000000015201541453020462 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/fpm-lint/app/0000775000175000017500000000000015201541453021242 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/fpm-lint/app/main2.f900000664000175000017500000000256715201541453022602 0ustar alastairalastairprogram main use, intrinsic :: iso_fortran_env, only : stderr => error_unit, stdout => output_unit use fpm_lint, only : lint_config, load_lint_config, lint_logger, new_logger, & & lint_data, get_argument use tomlf, only : toml_table, toml_load, toml_error, toml_context, toml_parser_config, & & toml_terminal implicit none logical, parameter :: color = .true. integer, parameter :: detail = 1 character(:), allocatable :: manifest type(toml_terminal) :: terminal type(toml_table), allocatable :: table type(toml_error), allocatable :: error type(toml_context) :: context type(lint_logger) :: logger type(lint_config) :: config terminal = toml_terminal(color) call get_argument(1, manifest) if (.not.allocated(manifest)) manifest = "fpm.toml" call toml_load(table, manifest, error=error, context=context, & & config=toml_parser_config(color=terminal, context_detail=detail)) call handle_error(error) call load_lint_config(config, table, context, terminal, error) call handle_error(error) call new_logger(logger) call lint_data(logger, config, table, context, terminal) call logger%show_log(stdout) contains subroutine handle_error(error) type(toml_error), intent(in), optional :: error if (present(error)) then write(stderr, '(a)') error%message stop 1 end if end subroutine handle_error end program main fortran-toml-0.5.0/doc/tutorial/fpm-lint/app/main1.f900000664000175000017500000000223715201541453022573 0ustar alastairalastairprogram main use, intrinsic :: iso_fortran_env, only : stderr => error_unit, stdout => output_unit use fpm_lint_config, only : lint_config, load_lint_config use fpm_lint_utils, only : get_argument use tomlf, only : toml_table, toml_load, toml_error, toml_context, toml_parser_config, & & toml_terminal implicit none logical, parameter :: color = .true. character(:), allocatable :: manifest type(toml_terminal) :: terminal type(toml_table), allocatable :: table type(toml_error), allocatable :: error type(toml_context) :: context type(lint_config) :: config terminal = toml_terminal(color) call get_argument(1, manifest) if (.not.allocated(manifest)) manifest = "fpm.toml" call toml_load(table, manifest, error=error, context=context, & & config=toml_parser_config(color=terminal)) call handle_error(error) call load_lint_config(config, table, context, terminal, error) call handle_error(error) contains subroutine handle_error(error) type(toml_error), intent(in), optional :: error if (present(error)) then write(stderr, '(a)') error%message stop 1 end if end subroutine handle_error end program main fortran-toml-0.5.0/doc/tutorial/fpm-lint/app/main3.f900000664000175000017500000000266615201541453022603 0ustar alastairalastairprogram main use, intrinsic :: iso_fortran_env, only : stderr => error_unit, stdout => output_unit use fpm_lint, only : lint_config, load_lint_config, lint_logger, new_logger, & & lint_data, lint_keys, get_argument use tomlf, only : toml_table, toml_load, toml_error, toml_context, toml_parser_config, & & toml_terminal implicit none logical, parameter :: color = .true. integer, parameter :: detail = 1 character(:), allocatable :: manifest type(toml_terminal) :: terminal type(toml_table), allocatable :: table type(toml_error), allocatable :: error type(toml_context) :: context type(lint_logger) :: logger type(lint_config) :: config terminal = toml_terminal(color) call get_argument(1, manifest) if (.not.allocated(manifest)) manifest = "fpm.toml" call toml_load(table, manifest, error=error, context=context, & & config=toml_parser_config(color=terminal, context_detail=detail)) call handle_error(error) call load_lint_config(config, table, context, terminal, error) call handle_error(error) call new_logger(logger) call lint_data(logger, config, table, context, terminal) call lint_keys(logger, config, context, terminal) call logger%show_log(stdout) contains subroutine handle_error(error) type(toml_error), intent(in), optional :: error if (present(error)) then write(stderr, '(a)') error%message stop 1 end if end subroutine handle_error end program main fortran-toml-0.5.0/doc/tutorial/fpm-lint/app/main0.f900000664000175000017500000000161215201541453022566 0ustar alastairalastairprogram main use, intrinsic :: iso_fortran_env, only : stderr => error_unit, stdout => output_unit use fpm_lint_utils, only : get_argument use tomlf, only : toml_table, toml_load, toml_error, toml_context, toml_parser_config implicit none logical, parameter :: color = .true. character(:), allocatable :: manifest type(toml_table), allocatable :: table type(toml_error), allocatable :: error type(toml_context) :: context call get_argument(1, manifest) if (.not.allocated(manifest)) manifest = "fpm.toml" call toml_load(table, manifest, error=error, context=context, & & config=toml_parser_config(color=color)) call handle_error(error) contains subroutine handle_error(error) type(toml_error), intent(in), optional :: error if (present(error)) then write(stderr, '(a)') error%message stop 1 end if end subroutine handle_error end program main fortran-toml-0.5.0/doc/tutorial/fpm-lint/src/0000775000175000017500000000000015201541453021251 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/fpm-lint/src/logger.f900000664000175000017500000000173015201541453023051 0ustar alastairalastairmodule fpm_lint_logger implicit none private public :: lint_logger, new_logger type :: log_message character(:), allocatable :: output end type log_message type :: lint_logger type(log_message), allocatable :: message(:) contains procedure :: add_message procedure :: show_log end type lint_logger contains subroutine new_logger(logger) type(lint_logger), intent(out) :: logger allocate(logger%message(0)) end subroutine new_logger subroutine add_message(logger, message) class(lint_logger), intent(inout) :: logger character(*), intent(in) :: message logger%message = [logger%message, log_message(message)] end subroutine add_message subroutine show_log(logger, io) class(lint_logger), intent(in) :: logger integer, intent(in) :: io integer :: it do it = 1, size(logger%message) write(io, '(a)') logger%message(it)%output end do end subroutine show_log end module fpm_lint_logger fortran-toml-0.5.0/doc/tutorial/fpm-lint/src/utils.f900000664000175000017500000000333615201541453022736 0ustar alastairalastair!> Misc utilities for the fpm-lint implementation module fpm_lint_utils implicit none private public :: resize public :: get_argument !> Resize a 1D array to a new size interface resize module procedure :: resize_ints end interface resize contains !> Reallocate list of integer pure subroutine resize_ints(var, n) !> Instance of the array to be resized integer, allocatable, intent(inout) :: var(:) !> Dimension of the final array size integer, intent(in), optional :: n integer, allocatable :: tmp(:) integer :: this_size, new_size integer, parameter :: initial_size = 8 if (allocated(var)) then this_size = size(var, 1) call move_alloc(var, tmp) else this_size = initial_size end if if (present(n)) then new_size = n else new_size = this_size + this_size/2 + 1 end if allocate(var(new_size)) if (allocated(tmp)) then this_size = min(size(tmp, 1), size(var, 1)) var(:this_size) = tmp(:this_size) deallocate(tmp) end if end subroutine resize_ints !> Obtain the command line argument at a given index subroutine get_argument(idx, arg) !> Index of command line argument, range [0:command_argument_count()] integer, intent(in) :: idx !> Command line argument character(len=:), allocatable, intent(out) :: arg integer :: length, stat call get_command_argument(idx, length=length, status=stat) if (stat == 0) then allocate(character(len=length) :: arg, stat=stat) end if if (stat == 0 .and. length > 0) then call get_command_argument(idx, arg, status=stat) if (stat /= 0) deallocate(arg) end if end subroutine get_argument end module fpm_lint_utils fortran-toml-0.5.0/doc/tutorial/fpm-lint/src/config.f900000664000175000017500000000541015201541453023036 0ustar alastairalastair!> Configuration data for the manifest linting module fpm_lint_config use tomlf, only : toml_table, toml_context, toml_terminal, toml_error, & & toml_stat, get_value implicit none !> Configuration for the manifest linting type :: lint_config !> Check package name logical :: package_name !> Check all key paths logical :: bare_keys end type lint_config contains !> Load the configuration for the linter from the package manifest subroutine load_lint_config(config, table, context, terminal, error) !> Configuration for the linter type(lint_config), intent(out) :: config !> TOML data structure representing the manifest type(toml_table), intent(inout) :: table !> Context describing the data structure type(toml_context), intent(in) :: context !> Terminal for output type(toml_terminal), intent(in) :: terminal !> Error handler type(toml_error), allocatable, intent(out) :: error integer :: origin, stat type(toml_table), pointer :: child1, child2, child3 call get_value(table, "extra", child1, origin=origin) if (.not.associated(child1)) then call make_error(error, context%report("The 'extra' table is missing.", & & origin, "expected table", color=terminal)) return end if call get_value(child1, "fpm", child2, origin=origin) if (.not.associated(child2)) then call make_error(error, context%report("The 'fpm' table is missing.", & & origin, "expected table", color=terminal)) return end if call get_value(child2, "lint", child3, origin=origin) if (.not.associated(child3)) then call make_error(error, context%report("The 'lint' table is missing.", & & origin, "expected table", color=terminal)) return end if call get_value(child3, "package-name", config%package_name, .true., & & stat=stat, origin=origin) if (stat /= toml_stat%success) then call make_error(error, context%report("Entry in 'package-name' must be boolean", & & origin, "expected boolean value", color=terminal)) return end if call get_value(child3, "bare-keys", config%bare_keys, .true., & & stat=stat, origin=origin) if (stat /= toml_stat%success) then call make_error(error, context%report("Entry in 'bare-key' must be boolean", & & origin, "expected boolean value", color=terminal)) return end if end subroutine load_lint_config !> Create an error message subroutine make_error(error, message) !> Error handler type(toml_error), allocatable, intent(out) :: error !> Message to be displayed character(len=*), intent(in) :: message allocate(error) error%message = message error%stat = toml_stat%fatal end subroutine make_error end module fpm_lint_config fortran-toml-0.5.0/doc/tutorial/fpm-lint/src/lint.f900000664000175000017500000001433715201541453022547 0ustar alastairalastair!> Linter for package manifests used with the Fortran package manager module fpm_lint use fpm_lint_config, only : lint_config, load_lint_config use fpm_lint_logger, only : lint_logger, new_logger use fpm_lint_utils, only : resize, get_argument use tomlf, only : toml_table, toml_context, toml_terminal, toml_error, toml_level, & & toml_key, get_value use tomlf_de_token, only : token_kind, stringify implicit none private public :: lint_config, load_lint_config public :: lint_logger, new_logger public :: lint_data public :: lint_keys public :: get_argument contains !> Entry point for linting the data structure representing the package manifest subroutine lint_data(logger, config, table, context, terminal) !> Instance of the logger type(lint_logger), intent(inout) :: logger !> Configuration for the linter type(lint_config), intent(in) :: config !> TOML data structure type(toml_table), intent(inout) :: table !> Context describing the data structure type(toml_context), intent(in) :: context !> Terminal for output type(toml_terminal), intent(in) :: terminal if (config%package_name) then check_package_name: block character(:), allocatable :: package_name integer :: origin call get_value(table, "name", package_name, origin=origin) if (.not.allocated(package_name)) then call logger%add_message(context%report( & "Package name entry is required in the top-level", & origin, & level=toml_level%info, color=terminal)) exit check_package_name end if if (context%token(origin)%kind /= token_kind%string) then call logger%add_message(context%report( & "Package name should not be a "//stringify(context%token(origin)), & origin, & "prefer string value (double quotes)", & level=toml_level%info, color=terminal)) end if if (verify(package_name, "abcdefghijklmnopqrstuvwxyz0123456789-") > 0) then call logger%add_message(context%report( & "Package name should be lowercase with dashes", & origin, & level=toml_level%info, color=terminal)) end if end block check_package_name end if end subroutine lint_data !> Entry point for linting the keys in the TOML document subroutine lint_keys(logger, config, context, terminal) !> Instance of the logger type(lint_logger), intent(inout) :: logger !> Configuration for the linter type(lint_config), intent(in) :: config !> Context describing the data structure type(toml_context), intent(in) :: context !> Terminal for output type(toml_terminal), intent(in) :: terminal integer :: it type(toml_key), allocatable :: list(:) call identify_keys(list, context) if (config%bare_keys) then do it = 1, size(list) associate(token => context%token(list(it)%origin)) if (token%kind /= token_kind%keypath) then call logger%add_message(context%report( & "String used in key path", & list(it)%origin, & "use bare key instead", & level=toml_level%info, color=terminal)) end if end associate end do end if end subroutine lint_keys !> Collect all key paths used in TOML document subroutine identify_keys(list, context) !> List of all keypaths in the TOML document type(toml_key), allocatable, intent(out) :: list(:) !> Context describing the data structure type(toml_context), intent(in) :: context integer, parameter :: table_scope = 1, array_scope = 2, value_scope = 3 integer :: it, top integer, allocatable :: scopes(:) allocate(list(0)) top = 0 call resize(scopes) ! Documents always start with a table scope call push_back(scopes, top, table_scope) do it = 1, context%top select case(context%token(it)%kind) case(token_kind%keypath) ! Record all key path associate(token => context%token(it)) list = [list, toml_key(context%source(token%first:token%last), it)] end associate case(token_kind%string, token_kind%literal) ! Record all strings used in key paths if (scopes(top) == table_scope) then associate(token => context%token(it)) list = [list, toml_key(context%source(token%first+1:token%last-1), it)] end associate end if case(token_kind%equal) ! Open value scope call push_back(scopes, top, value_scope) case(token_kind%lbrace) ! Open inline table scope call push_back(scopes, top, table_scope) case(token_kind%lbracket) ! Open array scope if (scopes(top) /= table_scope) then call push_back(scopes, top, array_scope) end if case(token_kind%newline) ! Close value scope in key-value pair call pop(scopes, top, value_scope) case(token_kind%rbrace) ! Close value and table scope in inline table call pop(scopes, top, value_scope) call pop(scopes, top, table_scope) case(token_kind%comma) ! Close value scope in inline table call pop(scopes, top, value_scope) case(token_kind%rbracket) ! Close array scope call pop(scopes, top, array_scope) end select end do contains !> Push a new scope onto the stack pure subroutine push_back(scopes, top, this_scope) !> Stack top integer, intent(inout) :: top !> Current stack of scopes integer, allocatable, intent(inout) :: scopes(:) !> Scope to push onto the stack integer, intent(in) :: this_scope top = top + 1 if (top > size(scopes)) call resize(scopes) scopes(top) = this_scope end subroutine push_back !> Remove a matching scope from the stack subroutine pop(scopes, top, this_scope) !> Stack top integer, intent(inout) :: top !> Current stack of scopes integer, allocatable, intent(inout) :: scopes(:) !> Scope to remove from the stack integer, intent(in) :: this_scope if (top > 0) then if (scopes(top) == this_scope) top = top - 1 end if end subroutine pop end subroutine identify_keys end module fpm_lint fortran-toml-0.5.0/doc/tutorial/fpm-lint/example/0000775000175000017500000000000015201541453022115 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/fpm-lint/example/2-dotted-keys.toml0000664000175000017500000000014615201541453025406 0ustar alastairalastairname = "demo" [dependencies] toml-f.git = "https://github.com/toml-f/toml-f" "toml-f".tag = "v0.2.3" fortran-toml-0.5.0/doc/tutorial/fpm-lint/example/0-invalid.toml0000664000175000017500000000010015201541453024564 0ustar alastairalastairname = "demo" [extra.fpm.lint] package-name = bare-keys = true fortran-toml-0.5.0/doc/tutorial/fpm-lint/example/1-camel-case.toml0000664000175000017500000000002315201541453025135 0ustar alastairalastairname = "fpmLinter" fortran-toml-0.5.0/doc/tutorial/fpm-lint/example/2-inline-table.toml0000664000175000017500000000014415201541453025513 0ustar alastairalastairname = "demo" [dependencies] toml-f = {git = "https://github.com/toml-f/toml-f", "tag" = "v0.2.3"} fortran-toml-0.5.0/doc/tutorial/fpm-lint/example/1-snake-case.toml0000664000175000017500000000002415201541453025156 0ustar alastairalastairname = "fpm_linter" fortran-toml-0.5.0/doc/tutorial/fpm-lint/example/0-incorrect.toml0000664000175000017500000000010715201541453025135 0ustar alastairalastairname = "demo" [extra.fpm.lint] package-name = "true" bare-keys = true fortran-toml-0.5.0/doc/tutorial/fpm-lint/fpm.toml0000664000175000017500000000043715201541453022145 0ustar alastairalastairname = 'fpm-lint' executable = [ {name = "fpm-lint0", main = "main0.f90"}, {name = "fpm-lint1", main = "main1.f90"}, {name = "fpm-lint2", main = "main2.f90"}, {name = "fpm-lint3", main = "main3.f90"}, ] [build] auto-executables = false [dependencies] toml-f.path = "../../.." fortran-toml-0.5.0/doc/tutorial/getting-started/0000775000175000017500000000000015201541453022041 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/getting-started/app/0000775000175000017500000000000015201541453022621 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/getting-started/app/readin.f900000664000175000017500000000144215201541453024404 0ustar alastairalastairprogram readin use reader, only : read_data use tomlf, only : toml_table, toml_parse, toml_error implicit none type(toml_table), allocatable :: table character(len=:), allocatable :: title real, allocatable :: spectrum(:) block integer :: io type(toml_error), allocatable :: error open(file="input.toml", newunit=io, status="old") call toml_parse(table, io, error) close(io) if (allocated(error)) then print '(a)', "Error: "//error%message stop 1 end if end block call read_data(table, title, spectrum) if (allocated(title)) then print '(a)', "Title: '"//title//"'" end if print '(*(g0, 1x))', "Entries:", size(spectrum) if (size(spectrum) > 0) then print '(*(g0, 1x))', "Spectrum:", spectrum end if end program readin fortran-toml-0.5.0/doc/tutorial/getting-started/app/defaults.f900000664000175000017500000000100515201541453024744 0ustar alastairalastairprogram defaults use reader, only : read_data use tomlf, only : toml_table implicit none type(toml_table), allocatable :: table character(len=:), allocatable :: title real, allocatable :: spectrum(:) table = toml_table() call read_data(table, title, spectrum) if (allocated(title)) then print '(a)', "Title: '"//title//"'" end if print '(*(g0, 1x))', "Entries:", size(spectrum) if (size(spectrum) > 0) then print '(*(g0, 1x))', "Spectrum:", spectrum end if end program defaults fortran-toml-0.5.0/doc/tutorial/getting-started/src/0000775000175000017500000000000015201541453022630 5ustar alastairalastairfortran-toml-0.5.0/doc/tutorial/getting-started/src/reader.f900000664000175000017500000000211015201541453024404 0ustar alastairalastairmodule reader use tomlf, only : toml_table, toml_array, get_value, len implicit none private public :: read_data contains subroutine read_data(table, title, spectrum) type(toml_table), intent(inout) :: table character(len=:), allocatable, intent(out) :: title real, allocatable, intent(out) :: spectrum(:) type(toml_table), pointer :: child type(toml_array), pointer :: array logical :: reverse integer :: ival ! Get character value from entry "title" call get_value(table, "title", title) ! Get subtable reference from entry "spectrum" call get_value(table, "spectrum", child) ! Get array reference from entry "data" call get_value(child, "data", array) ! Read all values from the data array allocate(spectrum(len(array))) do ival = 1, size(spectrum) call get_value(array, ival, spectrum(ival)) end do ! Data is stored in reverse order call get_value(child, "reverse", reverse, .false.) if (reverse) spectrum(:) = spectrum(size(spectrum):1:-1) end subroutine read_data end module reader fortran-toml-0.5.0/doc/tutorial/getting-started/fpm.toml0000664000175000017500000000006715201541453023523 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../.." fortran-toml-0.5.0/doc/tutorial/getting-started/input.toml0000664000175000017500000000012515201541453024073 0ustar alastairalastairtitle = "Example" spectrum.data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] fortran-toml-0.5.0/doc/tutorial/fpm-lint.rst0000664000175000017500000003341715201541453021224 0ustar alastairalastairBuilding a linter ================= .. sectionauthor:: Sebastian Ehlert <@awvwgk> .. image:: https://img.shields.io/badge/difficulty-beginner-brightgreen :alt: Difficulty: Beginner This tutorial will show how to use TOML Fortran to build a linter for your configuration files. Linters provide a way to encourage or enforce a certain style or flag up common usage errors. Target selection ---------------- This tutorial will look into finding lint in the package manifest from the Fortran package manager (`fpm `_). We will use its plugin mechanism to create a new subcommand called ``lint``. We start with setting up the package manifest for our linter: .. code-block:: toml :caption: fpm.toml name = "fpm-lint" version = "0.1.0" [dependencies] toml-f.git = "https://github.com/toml-lang/toml-f.git" Configuration of the linter --------------------------- To configure our linter we will use the `extra section `__ in the manifest which is specially reserved for tools integrating with fpm and boldly claim *extra.fpm.lint* as our configuration section. Using the package manifest provides us with two advantages, first this document will be present in all projects using fpm, second if we can read our configuration from the manifest, we are already sure it is valid TOML. .. code-block:: toml :caption: fpm.toml # ... [extra.fpm.lint] package-name = true bare-keys = true Now we will set up our main program to run the linter. .. literalinclude:: fpm-lint/app/main0.f90 :language: fortran :caption: app/main.f90 We create a utility module for the *get_argument* function used to retrieve the manifest name, in most cases we can default to *fpm.toml*, but for testing it is convenient to pass an argument. .. literalinclude:: fpm-lint/src/utils.f90 :language: fortran :caption: src/utils.f90 :lines: 1-5, 7, 13-15, 49-69 The first error source we can encounter stems from parsing the TOML document itself. This is outside of our responsibility to handle, still we want to check whether we can report the error correctly. .. literalinclude:: fpm-lint/example/0-invalid.toml :language: toml :caption: fpm.toml (invalid) Running the linter on this document will break with the following message produced by the *toml_load* procedure. .. ansi-block:: ❯ fpm run -- invalid.toml error: Invalid expression for value --> invalid.toml:4:15 | 4 | package-name = | ^ unexpected newline | With this case covered we proceed with reading the configuration for our linter. Our configuration from the package manifest will be stored in a *lint_config* type which we define in a separate module. Reading the configuration will happen from the root table, meaning we have to advance through several subtables first before we can process the options for our linter. We want to report errors with rich context information here as well, therefore we request the *origin* in every call to the *get_value* interface and produce a report using the *context* we obtained in the main program. .. literalinclude:: fpm-lint/src/config.f90 :language: fortran :caption: src/config.f90 For convenience, we defined a *make_error* routine to allocate the error handler and store our report from the context. At this point, we should check whether our error reporting works and run the linter on an incorrect TOML document. .. literalinclude:: fpm-lint/example/0-incorrect.toml :language: toml :caption: fpm.toml .. dropdown:: current main program Putting everything together in the main program should look like this. .. literalinclude:: fpm-lint/app/main1.f90 :language: fortran :caption: app/main.f90 Running our linter on this file will correctly flag this as an error since a string value is provided rather than a boolean value. .. ansi-block:: ❯ fpm run -- fpm.toml error: Entry in 'package-name' must be boolean --> fpm.toml:4:16-21 | 4 | package-name = "true" | ^^^^^^ expected boolean value | Finally, we define a logging mechanism to capture our actual linting messages which are not fatal. The logger provides two procedures, *add_message* to store a message and *show_log* to display all stored messages. .. literalinclude:: fpm-lint/src/logger.f90 :language: fortran :caption: src/logger.f90 Recommended package name ------------------------ As a first linting check we will inspect the package name, for this we will apply the following rules: 1. the package name should be a TOML bare key to not require quotes in *dependency* sections, characters like dots, colons, or slashes are not allowed 2. TOML generally favors lowercase dashed keys, therefore we will discourage capitalization (camelCase and PascalCase) as well as underscores (snake_case) 3. there are several ways to declare strings in TOML, we want to favor the normal string one An example of a package name we would disallow would be *fpmLinter* as seen in the manifest below. .. literalinclude:: fpm-lint/example/1-camel-case.toml :language: toml :caption: fpm.toml Let's start with our implementation of this check. For convenience we will reexport the other modules from the *fpm_lint* module, this allows one clean import in the main program. Then we define the *lint_data* procedure, where we first check whether the *name* key is present, if not we create a message at the *info* level and leave our block scope, as all further checks rely on the presence of the entry. We can now check whether the entry is provided as a string or maybe as something else, like a literal string, which we can flag. Furthermore, we verify that the package name uses only lowercase letters, numbers, and dashes with the *verify* intrinsic. .. literalinclude:: fpm-lint/src/lint.f90 :language: fortran :caption: src/lint.f90 :lines: 1-14, 16-66, 191 .. tip:: The ``toml_level`` parameter provides a statically initialized derived type enumerating all available report levels. Similarly, the ``token_kind`` parameter provides an enumeration of the token kinds. You can think of it as an enumerator with a proper namespace. .. dropdown:: current main program Putting everything together in the main program should look like this. .. literalinclude:: fpm-lint/app/main2.f90 :language: fortran :caption: app/main.f90 We check this on the camelCase package name from above and can find the following output. .. ansi-block:: ❯ fpm run -- fpm.toml info: Package name should be lowercase with dashes --> fpm.toml:1:8-18 | 1 | name = "fpmLinter" | ^^^^^^^^^^^ | .. admonition:: Exercise :class: note Add a check for the length of the package name, everything under three characters is probably a bad choice, so is a too long package name. Create an example to trigger the error with your new check. What happens if a too long camelCase package name is used? Bare key paths preferred ------------------------ TOML allows to quote keys, however this might become visually distracting if some keys are quoted and others are not. With our package name rule, there should not be the need to quote any keys even in dependency sections. To determine whether a string is used in the context of a key we need a way to identify all keys. We could check all entries in the data structures by implementing a visitor object which walks through all tables and checks the keys. However, this is somewhat inefficient and we can also miss keys that are not recorded. .. literalinclude:: fpm-lint/example/2-dotted-keys.toml :language: toml :caption: fpm.toml In this example, the second occurrence of the key ``toml-f`` will only reference the table but it is already defined the line before. The quotation marks are visually identifiable as lint and we need a programmatic way to flag this. Instead of working with the data structure, we will use the parser to record more tokens in the context. Rather than using the context to only report errors, we will use it to identify keys. This is done by increasing the *context_detail* option in the *config* keyword of the parser to one. Now all tokens except for whitespace and comments will be recorded. .. code-block:: fortran :caption: app/main.f90 call toml_load(table, manifest, error=error, context=context, & & config=toml_parser_config(color=color, context_detail=1)) .. tip:: Increasing the ``context_detail`` to two will also record whitespace and comments. This can be useful when writing checks for whitespace or indentation styles. Our linter pass will work as follows: 1. identifying all relevant keys in the manifest 2. check whether they are keypath tokens 3. create a report for any key that is a string or a literal Our implementation reflects this by first collecting an array of *toml_key* objects in *list* and then iterating over all entries checking whether they are the correct *token_kind*. .. literalinclude:: fpm-lint/src/lint.f90 :language: fortran :caption: src/lint.f90 :lines: 67-96 To create the list we need to implement the *identify_keys* procedure. The rules in TOML for key paths are simple: before an equal sign we can have key paths and keypath can only be present in table bodies or inline tables. This can be implemented by using a stack storing whether the current scope belongs in a table, array, or value. We will always push a new scope on the respective token opening it, *i.e.* a value is opened by an equal sign, an array by a right bracket, and an inline table by a right curly brace. To distinguish table headers from inline arrays we only push arrays on our stack after an equal sign. Finally, we default to a table scope if no other scope is present and we have collected all required rules to identify key paths. Similarly, we can identify the endings of the scopes. We then can check whether the current scope on the top of the stack allows key paths and record those in our list. .. literalinclude:: fpm-lint/src/lint.f90 :language: fortran :caption: src/lint.f90 :lines: 98-189 For convenience, we implement a *push_back* and *pop* function to add and remove scopes from our stack. The *pop* function will additionally perform a check whether we want to remove a matching scope and save us some repetition in the loop this way. In our utility module, we implement the *resize* procedure for an array of integers .. literalinclude:: fpm-lint/src/utils.f90 :language: fortran :caption: src/utils.f90 :lines: 1-48, 69 .. dropdown:: current main program Putting everything together in the main program should look like this. .. literalinclude:: fpm-lint/app/main3.f90 :language: fortran :caption: app/main.f90 At this point, we can now add a call in our main program to run the linter. .. ansi-block:: ❯ fpm run -- fpm.toml info: String used in key path --> fpm.toml:5:1-8 | 5 | "toml-f".tag = "v0.2.3" | ^^^^^^^^ use bare key instead | Now for something more tricky with an inline table to check whether our scoping rules are working correctly. .. literalinclude:: fpm-lint/example/2-inline-table.toml :language: toml :caption: fpm.toml Our linter can correctly identify the *tag* entry as a string in the key path context and produces the appropriate message. .. ansi-block:: ❯ fpm run -- fpm.toml info: String used in key path --> fpm.toml:4:53-57 | 4 | toml-f = {git = "https://github.com/toml-f/toml-f", "tag" = "v0.2.3"} | ^^^^^ use bare key instead | .. admonition:: Exercise :class: note Previously, we flagged the usage of a literal string as a value for the package name, however a package manifest can contain much more string values. Create a check for all string values in the manifest to ensure they use double-quotes. Collect string values (*string*, *literal*, *mstring*, and *mliteral*) from array and value scopes for this purpose. Can you make a meaningful suggestion if a literal string contains characters that must be escaped in a double-quoted string? Summary ------- This concludes the linting we wanted to implement for the fpm package manifest. For a feature-complete linter, the rule set to check for is usually growing with time and might also shift as new rules are adopted. Our linter currently provides only a few rules but has the potential to include more checks as the need arises. .. admonition:: Exercise Our output is currently in the order of the checks, rather than in the order of reports occurring in the TOML document. The output of the reports might become more intuitive if it was sorted according to the source lines. Record the first character in the output together with the messages in the logger. Have the logger sort the messages according to their order before printing them. .. important:: In this tutorial, you have learned how to report custom error messages in your TOML input data. You can now - report colorized error messages with rich context information - create error messages when reading a TOML data structure - control the details captured in the context describing the TOML document - check a TOML document based on the token information in the context fortran-toml-0.5.0/doc/tutorial/getting-started.rst0000664000175000017500000000756115201541453022604 0ustar alastairalastairGetting started =============== .. sectionauthor:: Sebastian Ehlert <@awvwgk> .. image:: https://img.shields.io/badge/difficulty-beginner-brightgreen :alt: Difficulty: Beginner This tutorial provides a gentle introduction to the use of TOML Fortran. It will deal with reading as well as creating TOML data structures using the high-level build interface and discuss how to obtain a data structure from a TOML document or turn a data structure to TOML document again. For this project we will be working with fpm, however you can use any build tool you are familar with, checkout the :ref:`integration guide ` to find a matching setup. We start with creating a minimal package manifest to use TOML Fortran in our fpm project. .. code-block:: toml :caption: fpm.toml name = "demo" [dependencies] toml-f.git = "https://github.com/toml-f/toml-f.git" The public TOML Fortran API is defined in the ``tomlf`` module, we will only use this module for this entire course. The main data structures we are going to interact with are ``toml_table`` and ``toml_array`` instances, which we can conveniently manipulate with the generic interface ``get_value``. .. literalinclude:: getting-started/src/reader.f90 :language: fortran :caption: src/reader.f90 Note that we declare the TOML data structure as mutable, *i.e.* ``intent(inout)`` rather than just ``intent(in)``, as the ``get_value`` interface can modify the data structure. We start with a simple test program which is not actually reading any TOML document, but just passing an empty table to our reader. .. literalinclude:: getting-started/app/defaults.f90 :language: fortran :caption: app/defaults.f90 The ``get_value`` interface for processing the TOML data structure ensures that the data structure is complete throughout the whole process of reading it and will add the requested nodes if there are not present or will fill them in with default values. Convince yourself that the empty table indeed changed while reading by passing a serializer to it. .. code-block:: fortran block use tomlf, only : toml_serialize print '(a)', "# Final TOML data structure" print '(a)', toml_serialize(table) end block ! Expected output: ! # Final TOML data structure ! [spectrum] ! data = [ ] ! reverse = false This behavior is very convenient because it allows us to define our default values while defining how we read the TOML data structure. .. note:: The ``get_value`` build interface is only one way of accessing the TOML data structure provided by TOML Fortran. It takes an opinionated approach towards reading and modifying the data structure, which is suitable the majority of applications. Now we will actually read a TOML document and pass it to our reader. .. code-block:: toml :caption: input.toml title = "Example" spectrum.data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] We adapt our command line driver to read the file ``input.toml`` and output the values as before .. literalinclude:: getting-started/app/readin.f90 :language: fortran :caption: app/readin.f90 Running the program with fpm shows that we were able to read the correct values from the document .. code-block:: text ❯ fpm run readin Title: 'Example' Entries: 10 Spectrum: 0.00000000 1.00000000 2.00000000 3.00000000 4.00000000 5.00000000 6.00000000 7.00000000 8.00000000 9.00000000 You can again use the serializer to write the final data structure, if you want to check whether the ``get_value`` interface has added default values. .. important:: In this tutorial you have learned out to read simple data structures from TOML documents. You can now - define the logic to read data from TOML structures - provide default values in your parser as you define the input structure - read an actual TOML document from a file - write TOML documents from your data structures fortran-toml-0.5.0/doc/tutorial/json.rst0000664000175000017500000004134615201541453020447 0ustar alastairalastair.. _json-lexer: Writing a custom lexer ====================== .. sectionauthor:: Sebastian Ehlert <@awvwgk> .. image:: https://img.shields.io/badge/difficulty-intermediate-yellow :alt: Difficulty: Intermediate Many programs already come with their input formats, switching to a different format requires establishing some way to get backward compatibility for older inputs. When transitioning to TOML Fortran reading of the input will use the provided TOML data structures. If the previous input format is sufficiently compatible, it can be parsed into a matching TOML data structure and allow to seamlessly use of the TOML format going forward while still providing compatibility for previously written inputs. This tutorial is meant to teach how lexing in TOML Fortran works and enable the reader to implement their custom lexer for their custom format. There is no guarantee that a custom input format can be ported by creating a custom lexer, since the format needs to fulfill some basic requirements, like providing typed values. For this tutorial, we will choose `JSON `_ as our input format and walk through all the steps to create a new lexer from scratch. .. note:: The choice of JSON for this tutorial is not a coincidence. TOML Fortran does implement this lexer to parse JSON files into TOML data structures to support the encoding tests in the validation suite of `BurntSushi/toml-test `_. .. important:: This tutorial makes partial use of the internal API of TOML Fortran. Identifying limitation ---------------------- Before we start to implement our custom lexer, we need to identify any limitations of the TOML data structures to represent our custom format. TOML documents always have a table at the document root, there is no way to represent a JSON array or single value in TOML. Furthermore, JSON supports the value type ``null``, which is not representable in TOML. We have two choices here, either we can flag ``null`` values as an invalid token or we can replace them in the lexer with something else like an empty table. Finally, there are other details we have to take into account, like how JSON is handling duplicate keys, for most of the implementation-dependent cases we will follow the rules TOML provides. This tutorial by no means aims for offering a fully compliant parser as we already fail for top-level arrays or ``null`` type values. For a custom format, this might be even more challenging, especially if the format is defined by only a single implementation. .. note:: Writing a compliant JSON parser can quickly become quite challenging (see `Parsing JSON is a Minefield `_). But format limitations can go both ways, there are of course also features in TOML we cannot express in JSON. However, since we want to map JSON to TOML and not the other way round we do not have to worry about limitations present in JSON. Every feature available in TOML representable in the previous input format will be an incentive to switch to the new format. .. note:: For the actual application of the JSON parser in the validation suite, this problem is solved by not using only strings to represent values and adding type annotations. In TOML Fortran these annotations are mapped back by pruning the read JSON data structure. The pruning is done via a visitor which is accepted after the data structure has been completely parsed. Creating the lexer ------------------ First, we start by creating a new subclass of the abstract base class (ABC) imported from the *tomlf_de_abc* module. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (json_lexer) :lines: 15-25,27-53 We start by creating a constructor to consume an input file and turn it into a string to advance through. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (new_lexer_from_file) :lines: 60-75 Using a formatted unit is more inefficient compared to reading the whole file with direct access, but needed in case we are dealing with the standard input. We make sure to error out if we get direct access or stream access units since we cannot reliably read those. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (new_lexer_from_unit) :lines: 77-114 Finally, we sometimes also need to read from a string, there we add a constructor which can create a lexer for a string input. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (new_lexer_from_string) :lines: 116-124 The parser might need access to some of the internal data of the lexer, which is done via the *get_info* procedure. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (get_info) :lines: 126-141 Identifying tokens ------------------ Now that we can instantiate the lexer we need to implement the possibility to recognize tokens, this is done with the *next* method. We start with creating the actual tokenization step in the *next_token* procedure, which we will call in the *next* method. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next_token) :lines: 162-167 As a first action, we will advance the internal state of the lexer by consuming the last token. For convenience, we save the position in the source string in the *pos* and *prev* variables. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next_token, continued) :lines: 169-174 The next thing we check is if we have exhausted the input string and if so we return the *end of file* token. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next_token, continued) :lines: 176-180 Now we can inspect the current character from the source string and decide which token it should be labeled. The character set is quite simple, we have to consider opening and closing brackets and braces, for arrays and tables, respectively, commas, colons, strings, and whitespace. We will be explicitly producing whitespace tokens here rather than skipping it since the parser can gracefully handle whitespace. However, we have to consider that newlines have semantical meaning in TOML while they are only considered whitespace in JSON and therefore we will only produce whitespace tokens. We use a select case statement to decide which token to produce. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next_token, continued) :lines: 182-227 To wrap up the lexing we will try to identify unknown tokens as well as possible trying to advance to the next terminating character. For the terminating characters, we choose whitespace as well as control characters and place those in the module scope. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (terminated) :lines: 55-56 .. note:: We are cheating a bit here since we declare the colon as an *equal* token. This way we can use the same lexer for both JSON and TOML and still have the same parsing rules. One special case to consider is literals, like strings numbers or booleans. To not clutter the logic here we create separate routines for parsing the respective literal values. For obtaining string values we will implement this as *next_string*. Here we cannot simply advance to the next quote character, since we need to handle escape characters gracefully. While doing so we can also ensure that the escape sequences found are valid and not malformed. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next_string) :lines: 229-267 Strings can only contain printable characters, therefore we check for valid string characters using a small *valid_string* function for each character. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (valid_string) :lines: 377-389 We also need to identify numbers, mapping to either integers or floats in TOML, which is done via *next_number*. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next_number) :lines: 269-330 To support boolean values we implement a *next_boolean* procedure. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next_boolean) :lines: 332-354 Finally, we also want to parse null values using the *next_null* procedure. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next_null) :lines: 356-375 With this logic available we can now generate all required tokens for parsing JSON. .. tip:: Moving most of the validation logic in the tokenization simplifies the actual extraction of the value as we have to deal with fewer edge cases. Now we can wrap up the *next* procedure, instead of directly returning the token we will make some adjustments to the token stream here. In general, this is the right place to buffer tokens, perform overflow checks, or detect unclosed groups, we will only use it to insert two additional tokens to inject a top-level key. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (next) :lines: 143-160 This will direct the parser to leave the root document where newlines are semantically relevant since we cannot produce such newline tokens in our JSON lexer. .. admonition:: Exercise The *nil* token will make the parser skip the respective value. If we want to support *null* values, how would we have to modify our lexer to produce for example an empty table ``{}`` instead, *i.e.* a *lbrace* and *rbrace* token? Extracting values ----------------- Before we can connect our lexer to the existing TOML parser we have to implement the extraction of the values. The parser itself will use the *extract* member functions to obtain values for the respective tokens and never directly access the character stream. To extract the string value we implement the *extract_string* procedure. We will also use the *extract_string* routine to catch the *keypath* token we inserted in the token stream and return the wanted dummy value. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (extract_string) :lines: 391-430 Similarly, we implement the *extract_integer*, instead of using an internal read, we implement the reading ourselves. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (extract_integer) :lines: 432-461 For floating point numbers implemented in *extract_float* we will just use an internal read. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (extract_float) :lines: 463-479 The last token we can produce and extract from our lexer is are boolean values, which we implement in *extract_boolean*. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (extract_boolean) :lines: 481-493 We create a mocked routine for *extract_datetime* since we cannot produce this token in JSON. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 (extract_datetime) :lines: 495-506 This provides our lexer with full functionality regarding the extraction of values needed for parsing and creating data structures. .. dropdown:: full source For completeness here is again the full source of our lexer implementation. .. literalinclude:: ../../test/compliance/json_lexer.f90 :language: fortran :caption: src/json_lexer.f90 :lines: 14- Verifying the lexer ------------------- We could start right into connecting our lexer with the parser, but we have not yet verified that the tokenization and value extraction work as expected. For this purpose, we will create some unit tests using the `test-drive `_ framework. As the entry point for our tester, we will use the standard wrapper for launching test suites. .. dropdown:: tester program Taken from the `test-drive`_ README .. literalinclude:: json/test/main.f90 :language: fortran :caption: test/main.f90 :lines: 14- Our actual test suite for the lexer will be based on a routine called *check_token*, which creates a new lexer from a string and retrieves all tokens while comparing them with a reference token stream. We then can implement our checks by providing a string and a list of tokens to see whether our lexer can identify the expected tokens correctly. For visualization, we use the *tomlf_diagnostic* module to label the tokens in the actual source string. .. literalinclude:: json/test/test_lexer.f90 :language: fortran :caption: test/test_lexer.f90 :lines: 14-29,30,33,40,42,45,48,50,59-70,219-234,243-251,274-281,301-308,143-150,203-210,380- These are only a couple of tests, we have much more cases to consider for a robust lexer. .. admonition:: Exercise Write at least ten more tests for edge cases in the lexer. Make sure to include invalid cases and ensure that even invalid tokens are generated correctly. Connecting to the parser ------------------------ Now that we have verified the tokenization process in our lexer we can connect our custom lexer to the default TOML parser. For this purpose we define convenience interfaces called *json_load* / *json_loads* similar to the available *toml_load* / *toml_loads* interfaces. Other than the TOML-related load interfaces, we will also use them to implement necessary post-processing steps for the data structure. .. literalinclude:: ../../test/compliance/json_parser.f90 :language: fortran :caption: src/json_parser.f90 :lines: 14-42 The *json_load* interface is implemented by *json_load_file* and *json_load_unit*. The former is a wrapper that is using the *new_lexer_from_file* constructor. .. literalinclude:: ../../test/compliance/json_parser.f90 :language: fortran :caption: src/json_parser.f90 (json_load_file) :lines: 53-77 The latter wraps the *new_lexer_from_unit* constructor. .. literalinclude:: ../../test/compliance/json_parser.f90 :language: fortran :caption: src/json_parser.f90 (json_load_unit) :lines: 79-103 Finally, we also provide *json_loads* by implementing *json_load_string* using our *new_lexer_from_string* constructor. .. literalinclude:: ../../test/compliance/json_parser.f90 :language: fortran :caption: src/json_parser.f90 (json_load_unit) :lines: 105-124 These wrappers so far are very straightforward, first setting up a lexer instance and invoking the *parse* procedure which will construct the actual parser instance and process the token stream. After a successful run, the *table* instance will be allocated, for the post-processing, we invoke the *prune* routine. .. literalinclude:: ../../test/compliance/json_parser.f90 :language: fortran :caption: src/json_parser.f90 (prune) :lines: 127-131,134-135,138 Where we effectively remove the first child from the root table and return is a polymorphic *toml_value*. This has the advantage that we can support arrays and values at the root level with our JSON loader. .. tip:: The user can dispatch the value using a *select type* construct or by creating a view using the *cast_to_table* / *cast_to_array* / *cast_to_keyval* functions. .. dropdown:: full source For completeness here is again the full source of our parser implementation. Note that this implementation also contains an implementation of a *toml_visitor* to prune type annotations used in the validation test suite to represent TOML values. .. literalinclude:: ../../test/compliance/json_parser.f90 :language: fortran :caption: src/json_parser.f90 :lines: 14- Summary ------- Now we have a working lexer that can tokenize JSON documents into TOML parsable tokens. The lexer implemented in TOML Fortran works on a similar construction, with the difference that the TOML grammar is much more complicated to parse than JSON. .. important:: In this tutorial, you have learned about the tokenization process used in TOML Fortran. You can now - implement a custom lexer based on the TOML tokens - verify your lexer against an expected token stream - adjust the token stream to direct the parsing process - add a post-processing step to prune the resulting data structure fortran-toml-0.5.0/doc/tutorial/index.rst0000664000175000017500000000110515201541453020572 0ustar alastairalastair.. _tutorial: Tutorial courses ================ This section contains self-contained courses teaching the usage of TOML Fortran. Each tutorial is a full example for a specific application covering several aspects of the library. .. admonition:: Contributions are welcome! :class: note If you have written on TOML Fortran and want to contribute to the documentation, feel free to fork the repository and submit a pull request. .. toctree:: :caption: Difficulty: Beginner getting-started fpm-lint .. toctree:: :caption: Difficulty: Intermediate json fortran-toml-0.5.0/doc/index.rst0000664000175000017500000001304615201541453016736 0ustar alastairalastair:sd_hide_title: true TOML Fortran library ==================== .. image:: _static/header.svg :alt: TOML Fortran :width: 100% .. image:: https://img.shields.io/badge/license-MIT%7CApache%202.0-blue :alt: License :target: https://opensource.org/licenses/Apache-2.0 .. image:: https://img.shields.io/github/v/release/toml-f/toml-f :alt: Version :target: https://github.com/toml-f/toml-f/releases/latest .. image:: https://github.com/toml-f/toml-f/workflows/CI/badge.svg :alt: Continuous Integration :target: https://github.com/toml-f/toml-f/actions .. image:: https://github.com/toml-f/toml-f/workflows/docs/badge.svg :alt: API docs :target: https://toml-f.github.io/toml-f .. image:: https://readthedocs.org/projects/toml-f/badge/?version=latest :target: https://toml-f.readthedocs.io :alt: Documentation Status .. image:: https://codecov.io/gh/toml-f/toml-f/branch/main/graph/badge.svg :alt: Coverage :target: https://codecov.io/gh/toml-f/toml-f This project provides a library to work with `TOML `_ files and data structures and allows to define data serialization and deserialization in Fortran. The currently supported TOML standard is `version 1.0.0 `__. The TOML Fortran project is hosted at GitHub at `toml-f/toml-f `__. .. grid:: 3 :gutter: 3 .. grid-item-card:: :class-card: sd-bg-light sd-rounded-3 :class-header: sd-text-primary sd-text-center sd-font-weight-bold sd-border-bottom-0 :link: tutorial :link-type: ref :shadow: none :octicon:`mortar-board` Tutorials ^^^ Guides and courses for using TOML with complete and self-contained examples. .. grid-item-card:: :class-card: sd-bg-light sd-rounded-3 :class-header: sd-text-primary sd-text-center sd-font-weight-bold sd-border-bottom-0 :link: recipe :link-type: ref :shadow: none :octicon:`book` Recipes ^^^ Examples and recipes for solving common tasks with TOML Fortran. .. grid-item-card:: :class-card: sd-bg-light sd-rounded-3 :class-header: sd-text-primary sd-text-center sd-font-weight-bold sd-border-bottom-0 :link: https://toml-f.github.io/toml-f :shadow: none :octicon:`gear` Reference ^^^ Generated documentation of all procedures and derived types available. .. grid-item-card:: :class-card: sd-bg-light sd-rounded-3 :class-header: sd-text-primary sd-text-center sd-font-weight-bold sd-border-bottom-0 :link: installation :link-type: ref :shadow: none :octicon:`arrow-down` Installation ^^^ Instructions for installing, updating or compiling TOML Fortran. .. grid-item-card:: :class-card: sd-bg-light sd-rounded-3 :class-header: sd-text-primary sd-text-center sd-font-weight-bold sd-border-bottom-0 :link: https://github.com/toml-f/toml-f :shadow: none :octicon:`mark-github` Repository ^^^ GitHub repository for the development of TOML Fortran. .. grid-item-card:: :class-card: sd-bg-light sd-rounded-3 :class-header: sd-text-primary sd-text-center sd-font-weight-bold sd-border-bottom-0 :link: external :link-type: ref :shadow: none :octicon:`link` External ^^^ External links to other resources, blogs, etc. .. card:: :class-body: sd-fs-4 sd-font-weight-bold :class-card: sd-mt-5 sd-px-5 sd-py-1 sd-rounded-3 sd-bg-light sd-text-center :shadow: none :octicon:`pin` News .. postlist:: 5 :excerpts: .. card:: :class-body: sd-fs-4 sd-font-weight-bold :class-card: sd-mt-5 sd-px-5 sd-py-1 sd-rounded-3 sd-bg-light sd-text-center :shadow: none :octicon:`rocket` Users TOML Fortran is used for example in the Fortran package manager (`fpm `_), a typical package manifest specified in TOML is shown below .. literalinclude:: ../fpm.toml :language: toml :caption: fpm.toml A collection of projects using TOML Fortran is shown below: .. grid:: 2 .. grid-item:: .. figure:: https://opengraph.githubassets.com/x/fortran-lang/fpm :alt: GitHub Preview :target: https://github.com/fortran-lang/fpm Fortran package manager and build system. .. grid-item:: .. figure:: https://opengraph.githubassets.com/x/tblite/tblite :alt: GitHub Preview :target: https://github.com/tblite/tblite Light-weight tight-binding framework. .. grid-item:: .. figure:: https://opengraph.githubassets.com/x/popelier-group/FEREBUS-v7 :alt: GitHub Preview :target: https://github.com/popelier-group/FEREBUS-v7 Gaussian Process Regression engine written in Fortran90 designed to produce models for atomistic simulations. .. grid-item:: .. figure:: https://opengraph.githubassets.com/x/lewisfish/signedMCRT :alt: GitHub Preview :target: https://github.com/lewisfish/signedMCRT Signed distance fields in Monte Carlo Radiative Transfer for modelling of smooth surfaces without the need to use triangle or similar meshes. Your project is using TOML Fortran but not listed here? Let us know of your work by opening a `discussion on GitHub `__ or create a `pull request `__ to add it to the selection above. .. toctree:: :maxdepth: 2 :hidden: Tutorials How-Tos References News external fortran-toml-0.5.0/doc/how-to/0000775000175000017500000000000015201541453016306 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/serde.rst0000664000175000017500000000302715201541453020144 0ustar alastairalastairSerializable base class ======================= This recipe shows how to create a serializable class based on TOML Fortran. Currently, TOML Fortran does not define such a base class itself, therefore we define a loader and dumper interface for turning a file or connected unit into a data structure. The abstract base class will implement the processing of the file or unit to a TOML data structure and pass it to a deferred procedure which the implementing class uses to define its mapping from and back to the TOML data structure. This way an easily round-tripable data structure can be created and used in a variety of contexts. .. note:: TOML Fortran might provide such abstract base class in the future natively. The base class can be defined as .. literalinclude:: serde/src/serde_class.f90 :caption: src/serde_class.f90 :language: fortran We also define a convenience error handler which holds the error message and signals its error status by its allocation state. .. literalinclude:: serde/src/serde_error.f90 :caption: src/serde_error.f90 :language: fortran An example for a serializable class based on the above base class is given below. .. literalinclude:: serde/src/demo.f90 :caption: src/demo.f90 :language: fortran The defined data class can in an application easily be loaded from a file, while the actual implementation does not have to deal with getting the TOML data structure from the file but can assume that if the configuration file was valid TOML it will be provided with a data structure to read from. fortran-toml-0.5.0/doc/how-to/datetime.rst0000664000175000017500000000377115201541453020644 0ustar alastairalastairDate time compatibility ======================= TOML values can represent date times and are also accessible via the build interfaces *get_value* and *set_value*. However, the internal representation of a date time value might not be compatible with other libraries dealing with date times. This recipes show how the build interface can be extended to also support common date time libraries. `datetime-fortran `_ --------------------------------------------------------------------------- The *datetime-fortran* library provides an opaque object representing a date time value and several features for manipulating it. An fpm project using both TOML Fortran and *datetime-fortran* can be setup using the following dependencies section in the manifest. .. code-block:: toml [dependencies] toml-f.git = "https://github.com/toml-f/toml-f.git" datetime.git = "https://github.com/wavebitscientific/datetime-fortran.git" To define a compatibility between the two derived types representing a date time value we extend the *get_value* and *set_value* generic interfaces. .. literalinclude:: datetime/datetime-fortran/src/compat.f90 :language: fortran :caption: src/compat.f90 `M_time `_ ----------------------------------------------- The *M_time* library provides a transparent object representing a date time value, with a rich functional and object oriented API for inspecting and manipulating it. An fpm project using both TOML Fortran and *M_time* can be setup using the following dependencies section in the manifest. .. code-block:: toml [dependencies] toml-f.git = "https://github.com/toml-f/toml-f.git" M_time.git = "https://github.com/urbanjost/M_time.git" To define a compatibility between the two derived types representing a date time value we extend the *get_value* and *set_value* generic interfaces. .. literalinclude:: datetime/M_time/src/compat.f90 :language: fortran :caption: src/compat.f90 fortran-toml-0.5.0/doc/how-to/jonquil.rst0000664000175000017500000000336615201541453020531 0ustar alastairalastair.. _jonquil: Compatibility with JSON ======================= The Jonquil project provides a flavor of TOML Fortran which can handle JSON. It is available from the `toml-f/jonquil config.toml:1:12-18 | 1 | timestep = "large" | ^^^^^^^ expected real value | Now we also have to handle the case where the value can be read correctly, but is incorrect for our application, like a negative timestep. .. literalinclude:: error/single/src/config.f90 :language: fortran :caption: src/config.f90 :lines: 35-39 The origin information will still be available and allow us to make a rich report about the error in the input. .. literalinclude:: error/single/example2.toml :language: toml :caption: config.toml The resulting error message is shown below. .. ansi-block:: error: Timestep must be positive --> fpm.toml:1:12-15 | 1 | timestep = -0.1 | ^^^^ expected positive real value | .. note:: Each TOML data structure has an *origin* attribute, which can be used together with the report function of the context. In case the origin cannot be mapped to a single token, *e.g.* for the root table, the value of the origin will be zero. The report function will only produce labels for non-zero origins and gracefully ignore data without origin in the current context. The reporting function is not limited to errors, it can also produce warnings or informational messages. For this purpose, we select the appropriate ``toml_level`` for the report. .. literalinclude:: error/single/src/config.f90 :language: fortran :caption: src/config.f90 :lines: 41-44 .. tip:: The ``toml_level`` parameter provides a statically initialized derived type enumerating all available report levels. You can think of it as an enumerator with a proper namespace. We can test this for the following example. .. literalinclude:: error/single/example3.toml :language: toml :caption: config.toml The resulting warning is shown below. .. ansi-block:: warning: Large timesteps can lead to instable simulations --> config.toml:1:12-16 | 1 | timestep = 100.0 | ^^^^^ | .. dropdown:: full source The full *demo_config* module is given here. .. literalinclude:: error/single/src/config.f90 :language: fortran :caption: src/dependency.f90 The driver for running the examples is given below. .. literalinclude:: error/single/app/main.f90 :language: fortran :caption: app/main.f90 Multiline reports ----------------- In some cases, multiple labels are required to express the context of the report correctly. This feature is available with the context object, by providing the origin of the two data structures in the reporting function. An example of this is the dependency table in fpm, where we can either provide a local dependency using the *path* key or a remote dependency using the *git* key, but not both at the same time. We declare a simple dummy dependency storing only the dependency name for demonstration purposes. .. literalinclude:: error/multi/src/dependency.f90 :language: fortran :caption: src/dependency.f90 :lines: 6-10 We iterate over the list of all subtables in the dependency table and read the actual dependency. In case an entry is not a subtable we will raise an error, since a package manifest can contain multiple dependency tables, we will report which table we are currently in as additional context. .. literalinclude:: error/multi/src/dependency.f90 :language: fortran :caption: src/dependency.f90 :lines: 14-39 To provide the *dependencies* table we create a simple driver to read a TOML document. .. literalinclude:: error/multi/app/main.f90 :language: fortran :caption: app/main.f90 An example triggering the error is shown below. .. literalinclude:: error/multi/example1.toml :language: toml :caption: fpm.toml Running this example will produce the following error showing lines 1 and 3 of our example input. .. ansi-block:: error: All entries must be subtables --> fpm.toml:3:1-6 | 1 | [dependencies] | ------------ required for this table : 3 | toml-f = "^0.3.0" | ^^^^^^ must be a subtable | Now we want to implement the actual conflicting case described above. Here we just read the two strings from the *git* and *path* entry. Note that the *get_value* interface will not allocate the string if no value is present, which allows to conveniently check for success via allocation status of the strings. .. literalinclude:: error/multi/src/dependency.f90 :language: fortran :caption: src/dependency.f90 :lines: 41-75 To preserve the order from the input we can compare the *origin* values of the two retrieved strings and produce the appropriate error message. In this example, the *git* entry was defined first and a conflicting *path* entry is provided afterward. .. literalinclude:: error/multi/example2.toml :language: toml :caption: fpm.toml The order is reported correctly in the produced error message shown below. .. ansi-block:: error: Remote dependency cannot have local path --> fpm.toml:3:15-36 | 2 | toml-f.git = "https://github.com/toml-f/toml-f.git" | -------------------------------------- remote dependency already defined 3 | toml-f.path = "./subprojects/toml-f" | ^^^^^^^^^^^^^^^^^^^^^^ cannot have local path | The other way round is also possible as shown in this example. .. literalinclude:: error/multi/example3.toml :language: toml :caption: fpm.toml The error message is adjusted accordingly and now reports a conflicting *git* entry to the already defined *path* entry. .. ansi-block:: error: Local dependency cannot have remote repository --> fpm.toml:3:14-51 | 2 | toml-f.path = "./subprojects/toml-f" | ---------------------- local dependency already defined 3 | toml-f.git = "https://github.com/toml-f/toml-f.git" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot have remote repository | .. dropdown:: full source The full *demo_dependency* module is given provided below. .. literalinclude:: error/multi/src/dependency.f90 :language: fortran :caption: src/dependency.f90 The driver for the examples is given here. .. literalinclude:: error/multi/app/main.f90 :language: fortran :caption: app/main.f90 Color support ------------- All reports also support colorful terminal output. For this purpose, we can use the provided *toml_terminal* which can be instantiated with color support. .. code-block:: fortran block use tomlf, only : toml_terminal type(toml_terminal) :: terminal terminal = toml_terminal(.true.) end block To activate the color support for error messages produced in the load interface the optional argument *config* takes a *toml_parser_config* instance. .. code-block:: fortran call toml_load(table, filename, config=toml_parser_config(color=.true.), error=error) Alternatively, an instance of a *toml_terminal* can be passed to the *toml_parser_config* constructor. For working with the *context* instance returned by the load interface we need a terminal to activate the colorful output passed to the optional *color* argument. .. code-block:: fortran print '(a)', context%report("Cannot read timestep", & & origin, "expected real value", color=toml_terminal(.true.)) The *terminal* can also be used to colorize regular text output. .. code-block:: fortran block use tomlf_terminal, only : toml_terminal, operator(//), operator(+) type(toml_terminal) :: terminal terminal = toml_terminal(.true.) print '(a)', (terminal%fg_red + terminal%bold) // "red bold text" // terminal%reset end block If the terminal is not initialized or the color support is explicitly disabled by passing ``.false.`` to the constructor, the output will be plain text. fortran-toml-0.5.0/doc/how-to/jonquil/0000775000175000017500000000000015201541453017767 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/jonquil/app/0000775000175000017500000000000015201541453020547 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/jonquil/app/main.f900000664000175000017500000000224315201541453022014 0ustar alastairalastairprogram demo use jonquil, only : json_loads, json_value, json_object, json_error, cast_to_object, & & get_value implicit none class(json_value), allocatable :: val type(json_object), pointer :: object type(json_error), allocatable :: error integer :: ival call json_loads(val, '{"a":1,"b":2}', error=error) if (allocated(error)) then print '(a)', error%message stop end if object => cast_to_object(val) if (associated(object)) then call get_value(object, "a", ival) print '(a,1x,i0)', "a is", ival call get_value(object, "b", ival) print '(a,1x,i0)', "b is", ival end if block use tomlf, only : toml_table, toml_array, add_array, set_value, toml_serialize integer :: it type(toml_table), pointer :: table type(toml_array), pointer :: array ! Add an array to the object call add_array(object, "c", array) call set_value(array, [-1, 0, 1]) ! Create a new subobject / subtable call get_value(object, "d", table, requested=.true.) call set_value(table, "sub", "table") print '(a)', "# representation in TOML land" print '(a)', toml_serialize(object) end block end program demo fortran-toml-0.5.0/doc/how-to/jonquil/fpm.toml0000664000175000017500000000015515201541453021447 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../.." jonquil.git = "https://github.com/toml-f/jonquil.git" fortran-toml-0.5.0/doc/how-to/index.rst0000664000175000017500000000077515201541453020160 0ustar alastairalastair.. _recipe: How-to guides ============= This section contains practical guides for working with TOML Fortran. Each recipe is tailored for a specific problem and use case it is trying to solve. .. admonition:: Contributions are welcome! :class: note If you have written on TOML Fortran and want to contribute to the documentation, feel free to fork the repository and submit a pull request. .. toctree:: installation integration table array error datetime serde jonquil fortran-toml-0.5.0/doc/how-to/error/0000775000175000017500000000000015201541453017437 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/error/multi/0000775000175000017500000000000015201541453020571 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/error/multi/app/0000775000175000017500000000000015201541453021351 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/error/multi/app/main.f900000664000175000017500000000141515201541453022616 0ustar alastairalastairprogram demo use tomlf, only : toml_table, toml_error, toml_context, toml_load, get_value use demo_dependency, only : dependency_type, load_dependencies implicit none type(dependency_type), allocatable :: deps(:) character(128) :: arg call get_command_argument(1, arg) block type(toml_table), allocatable :: table type(toml_error), allocatable :: error type(toml_context) :: context type(toml_table), pointer :: child call toml_load(table, trim(arg), context=context, error=error) if (allocated(error)) then print '(a)', error%message stop 1 end if call get_value(table, "dependencies", child) if (associated(child)) then call load_dependencies(deps, child, context) end if end block end program demo fortran-toml-0.5.0/doc/how-to/error/multi/example3.toml0000664000175000017500000000015015201541453023200 0ustar alastairalastair[dependencies] toml-f.path = "./subprojects/toml-f" toml-f.git = "https://github.com/toml-f/toml-f.git" fortran-toml-0.5.0/doc/how-to/error/multi/src/0000775000175000017500000000000015201541453021360 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/error/multi/src/dependency.f900000664000175000017500000000513415201541453024021 0ustar alastairalastair!> Demo module for fpm-like dependency reading module demo_dependency use tomlf, only : toml_table, toml_context, toml_key, get_value implicit none !> Dummy dependency type storing only the name of the dependency type :: dependency_type !> The name of the dependency character(:), allocatable :: name end type dependency_type contains !> Load a list of dependencies from a table subroutine load_dependencies(dependencies, table, context) !> List of dependencies type(dependency_type), allocatable, intent(out) :: dependencies(:) !> TOML data structure type(toml_table), intent(inout) :: table !> Context for error messages type(toml_context), intent(in) :: context integer :: it, origin type(toml_key), allocatable :: list(:) type(toml_table), pointer :: child call table%get_keys(list) allocate(dependencies(size(list))) do it = 1, size(list) call get_value(table, list(it), child, origin=origin) if (.not.associated(child)) then print '(a)', context%report("All entries must be subtables", & & origin, table%origin, "must be a subtable", "required for this table") stop 1 end if call load_dependency(dependencies(it), list(it), child, context) end do end subroutine load_dependencies !> Load a single dependency from a table subroutine load_dependency(dependency, name, table, context) !> Information about the dependency type(dependency_type), intent(out) :: dependency !> Name of the dependency type(toml_key), intent(in) :: name !> TOML data structure type(toml_table), intent(inout) :: table !> Context for error messages type(toml_context), intent(in) :: context integer :: git_origin, path_origin character(:), allocatable :: git, path type(toml_key), allocatable :: list(:) dependency%name = name%key call table%get_keys(list) call get_value(table, "git", git, origin=git_origin) call get_value(table, "path", path, origin=path_origin) if (allocated(git) .and. allocated(path)) then if (git_origin < path_origin) then print '(a)', context%report("Remote dependency cannot have local path", & & path_origin, git_origin, & & "cannot have local path", "remote dependency already defined") else print '(a)', context%report("Local dependency cannot have remote repository", & & git_origin, path_origin, & & "cannot have remote repository", "local dependency already defined") end if stop 1 end if end subroutine load_dependency end module demo_dependency fortran-toml-0.5.0/doc/how-to/error/multi/example1.toml0000664000175000017500000000015015201541453023176 0ustar alastairalastair[dependencies] json-fortran.git = "https://github.com/jacobwilliams/json-fortran.git" toml-f = "^0.3.0" fortran-toml-0.5.0/doc/how-to/error/multi/example2.toml0000664000175000017500000000015015201541453023177 0ustar alastairalastair[dependencies] toml-f.git = "https://github.com/toml-f/toml-f.git" toml-f.path = "./subprojects/toml-f" fortran-toml-0.5.0/doc/how-to/error/multi/fpm.toml0000664000175000017500000000007215201541453022247 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." fortran-toml-0.5.0/doc/how-to/error/single/0000775000175000017500000000000015201541453020720 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/error/single/app/0000775000175000017500000000000015201541453021500 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/error/single/app/main.f900000664000175000017500000000113015201541453022737 0ustar alastairalastairprogram demo use tomlf, only : toml_table, toml_error, toml_context, toml_load use demo_config, only : config_type, load_config implicit none type(config_type) :: config character(128) :: arg call get_command_argument(1, arg) block type(toml_table), allocatable :: table type(toml_error), allocatable :: error type(toml_context) :: context call toml_load(table, trim(arg), context=context, error=error) if (allocated(error)) then print '(a)', error%message stop 1 end if call load_config(config, table, context) end block end program demo fortran-toml-0.5.0/doc/how-to/error/single/example3.toml0000664000175000017500000000002115201541453023324 0ustar alastairalastairtimestep = 100.0 fortran-toml-0.5.0/doc/how-to/error/single/src/0000775000175000017500000000000015201541453021507 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/error/single/src/config.f900000664000175000017500000000256315201541453023302 0ustar alastairalastair!> Module for reading in configuration data module demo_config use tomlf, only : toml_table, toml_context, toml_level, get_value implicit none !> Configuration data type :: config_type !> Time step for simulation real :: timestep end type config_type !> Threshold for warning on large time step real, parameter :: large_timestep = 10.0 contains !> Load configuration data from TOML data structure subroutine load_config(config, table, context) !> Instance of the configuration data type(config_type), intent(out) :: config !> TOML data structure type(toml_table), intent(inout) :: table !> Context for reporting errors type(toml_context), intent(in) :: context integer :: stat, origin call get_value(table, "timestep", config%timestep, 0.5, stat=stat, origin=origin) if (stat /= 0) then print '(a)', context%report("Cannot read timestep", & & origin, "expected real value") stop 1 end if if (config%timestep <= 0) then print '(a)', context%report("Timestep must be positive", & & origin, "expected positive value") stop 1 end if if (config%timestep > large_timestep) then print '(a)', context%report("Large timesteps can lead to instable simulations", & & origin, level=toml_level%warning) end if end subroutine load_config end module demo_config fortran-toml-0.5.0/doc/how-to/error/single/example1.toml0000664000175000017500000000002315201541453023324 0ustar alastairalastairtimestep = "large" fortran-toml-0.5.0/doc/how-to/error/single/example2.toml0000664000175000017500000000002015201541453023322 0ustar alastairalastairtimestep = -0.1 fortran-toml-0.5.0/doc/how-to/error/single/fpm.toml0000664000175000017500000000007215201541453022376 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." fortran-toml-0.5.0/doc/how-to/array.rst0000664000175000017500000001032115201541453020153 0ustar alastairalastairWorking with arrays =================== TOML supports *array* data types: flat, ordered containers holding multiple values. Arrays are dynamically sized (*i.e.* do not need specify the number of elements before using an array) and can contain elements of any type supported by TOML. The following program parses and stores an array of integers then prints the values to stdout: .. literalinclude:: array/array/app/parse_array.f90 :caption: app/parse_array.f90 :language: Fortran The simplest way to parse an array-valued key in TOML Fortran is to use the ``get_value`` interface, which has an overload to handle arrays. First, we need to use a temporary variable ``arr`` (of type ``toml_array``), as the array's elements can be of potentially any type. ``arr`` is automatically associated and allocated by ``get_value``, which we can use to determine how much space is needed to store the array's contents. Finally, we must iterate over all elements of the temporary ``toml_array`` and assign them to an element in our final data structure ``arr_data`` (which is a standard Fortran array). As a test, let's run the above program using the following TOML table (``array.toml``): .. code-block:: toml :caption: array.toml data=[1,2,3,4] This produces the following output: .. code-block:: text data = 1 2 3 4 As mentioned above, TOML arrays can contain elements of any type supported by TOML. The TOML standard even supports heterogeneous arrays containing elements of multiple different types. TOML Fortran supports this through the generic ``toml_array`` type. Elements of ``toml_array`` may be of any value but require extra processing before they can be used in most Fortran programs, since Fortran arrays must only contain elements of a single type known at compile time. This is achieved through the generic ``get_value`` interface, which automatically coerces elements of the ``toml_array`` to the correct type based on the variable it is copied to. .. tip:: The value in ``get_value`` is an ``intent(out)`` argument, in case the input and output parameter are incompatible, it is not initialized and the actual value is dependent on the compiler settings. By passing an integer value to the optional *stat* argument, the procedure will return a non-zero value to indicate an error. Accessing whole arrays ---------------------- If the entries of an array are all of the same type or can be safely casted to a common type it is possible to retrieve a whole array using a single call to ``get_value``. .. literalinclude:: array/whole/app/main.f90 :caption: app/parse_array.f90 :language: Fortran The build interface will allocate the array to the correct size and then iterate over all elements and assign them to the array. Accessing nested arrays ----------------------- TOML arrays can be nested to produce an *array-of-arrays*. Both the top-level and nested child arrays are potentially variable-length and can contain any TOML data-type (including further nested arrays). The following program reads a TOML file with an array-of-arrays nested a single level deep, and prints its values to stdout: .. literalinclude:: array/nested/app/nested_array.f90 :caption: app/nested_array.f90 :language: Fortran First, we need to call ``get_value`` to get a pointer to the top-level array then iterate through its elements, calling ``get_value`` on each to get a pointer to the child array. Finally, we iterate through the individual sub-arrays and process their elements one-by-one. This approach has the advantage of being able to deal with nested arrays of arbitrary length, such as the TOML table below: .. code-block:: toml data=[[1,2,3], [4,5], [6]] Which produces the following output: .. code-block:: text data(1,1) = 1 data(1,2) = 2 data(1,3) = 3 data(2,1) = 4 data(2,2) = 5 data(3,1) = 6 .. tip:: A TOML array-of-arrays does not necessarily map cleanly to a regular Fortran multidimensional array. Multidimensional Fortran arrays must be rectangular (*i.e.* rows must contain the same number of elements), so an array-of-arrays with variable sized nested arrays cannot be directly mapped to a primitive Fortran type and must be represented using a compound data type. fortran-toml-0.5.0/doc/how-to/table/0000775000175000017500000000000015201541453017375 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/table/nested/0000775000175000017500000000000015201541453020657 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/table/nested/src/0000775000175000017500000000000015201541453021446 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/table/nested/src/input.f900000664000175000017500000001273315201541453023133 0ustar alastairalastair!> Input model for a computational chemistry simulation program module demo_input use demo_error, only : error_type use tomlf, only : toml_table, toml_stat, get_value implicit none private public :: simulation_input !> Input for the Slater-Koster integral tables type :: skf_input !> Format string to find Slater-Koster files character(len=:), allocatable :: format_string ! ... more settings for the Slater-Koster input end type skf_input !> Input for the self-consistent charge iterations type :: scc_input !> Convergence tolerance for energy minimization real :: tolerance ! ... more settings for the SCC input end type scc_input !> Input for DFTB Hamiltonian type :: dftb_input !> Self-consistent field specific settings type(scc_input), allocatable :: scc !> Slater-Koster table specific settings type(skf_input) :: skf ! ... more settings for the DFTB input end type dftb_input !> Input for the Hamiltonian used in the simulation type :: hamiltonian_input !> DFTB Hamiltonian specific settings type(dftb_input), allocatable :: dftb ! ... more settings for the Hamiltonian input end type hamiltonian_input !> Input for analysis of Hamiltonian type :: analysis_input !> Evaluate derivatives of energy expression logical :: calculate_forces ! ... more settings for the analysis input end type analysis_input !> Input for complete simulation type :: simulation_input !> Hamiltonian used for simulation, always needed type(hamiltonian_input) :: hamiltonian !> Analysis to run after successful Hamiltonian evaluation type(analysis_input), allocatable :: analysis ! ... more settings for the simulation input end type simulation_input contains !> Read root document subroutine read_simulation(error, input, table) !> Error handler type(error_type), allocatable :: error !> Simulation input to be read type(simulation_input), intent(out) :: input !> Data structure type(toml_table), intent(inout) :: table type(toml_table), pointer :: child integer :: stat call get_value(table, "hamiltonian", child) if (.not.associated(child)) then call fatal_error(error, "No hamiltonian section found in input file") return end if call read_hamiltonian(error, input%hamiltonian, child) if (allocated(error)) return call get_value(table, "analysis", child, requested=.false., stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "No analysis section found in input file") return end if if (associated(child)) then allocate(input%analysis) call read_analysis(error, input%analysis, child) if (allocated(error)) return end if ! ... read further values end subroutine read_simulation !> Read Hamiltonian from node subroutine read_hamiltonian(error, input, table) !> Error handler type(error_type), allocatable :: error !> Hamiltonian input to be read type(hamiltonian_input), intent(out) :: input !> Data structure type(toml_table), intent(inout) :: table type(toml_table), pointer :: child call get_value(table, "dftb", child, requested=.false.) if (associated(child)) then allocate(input%dftb) call read_dftb(error, input%dftb, child) if (allocated(error)) return end if ! ... read further values end subroutine read_hamiltonian !> Read DFTB Hamiltonian from node subroutine read_dftb(error, input, table) !> Error handler type(error_type), allocatable :: error !> DFTB Hamiltonian input to be read type(dftb_input), intent(out) :: input !> Data structure type(toml_table), intent(inout) :: table type(toml_table), pointer :: child call get_value(table, "scc", child, requested=.false.) if (associated(child)) then allocate(input%scc) call read_scc(error, input%scc, child) if (allocated(error)) return end if call get_value(table, "skf", child) if (.not.associated(child)) then call fatal_error(error, "No skf section found in dftb table") return end if call read_skf(error, input%skf, child) if (allocated(error)) return ! ... read further values end subroutine read_dftb !> Read SCC from node subroutine read_scc(error, input, table) !> Error handler type(error_type), allocatable :: error !> SCC input to be read type(scc_input), intent(out) :: input !> Data structure type(toml_table), intent(inout) :: table call get_value(table, "tolerance", input%tolerance, 1.0e-6) ! ... read further values end subroutine read_scc !> Read Slater-Koster files from node subroutine read_skf(error, input, table) !> Error handler type(error_type), allocatable :: error !> Slater-Koster input to be read type(skf_input), intent(out) :: input !> Data structure type(toml_table), intent(inout) :: table call get_value(table, "format-string", input%format_string, "{}-{}.skf") ! ... read further values end subroutine read_skf !> Read analysis from node subroutine read_analysis(error, input, table) !> Error handler type(error_type), allocatable :: error !> Analysis input to be read type(analysis_input), intent(out) :: input !> Data structure type(toml_table), intent(inout) :: table call get_value(table, "calculate-forces", input%calculate_forces, .false.) ! ... read further values end subroutine read_analysis end module demo_input fortran-toml-0.5.0/doc/how-to/table/nested/src/error.f900000664000175000017500000000233115201541453023116 0ustar alastairalastair!> Central registry for error codes module demo_error implicit none private public :: error_type, fatal_error !> Possible error codes type :: enum_stat !> Successful run integer :: success = 0 !> Internal error: integer :: fatal = 1 end type enum_stat !> Actual enumerator for return states type(enum_stat), parameter :: demo_stat = enum_stat() !> Error message type :: error_type !> Error code integer :: stat !> Payload of the error character(len=:), allocatable :: message end type error_type contains !> A fatal error is encountered subroutine fatal_error(error, message, stat) !> Instance of the error type(error_type), allocatable, intent(out) :: error !> A detailed message describing the error and (optionally) offering advice character(len=*), intent(in), optional :: message !> Overwrite of the error code integer, intent(in), optional :: stat allocate(error) if (present(stat)) then error%stat = stat else error%stat = demo_stat%fatal end if if (present(message)) then error%message = message else error%message = "Fatal error" end if end subroutine fatal_error end module demo_error fortran-toml-0.5.0/doc/how-to/table/nested/fpm.toml0000664000175000017500000000007215201541453022335 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." fortran-toml-0.5.0/doc/how-to/table/keys/0000775000175000017500000000000015201541453020350 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/table/keys/src/0000775000175000017500000000000015201541453021137 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/table/keys/src/requirements.f900000664000175000017500000000544415201541453024211 0ustar alastairalastair!> Defines a simple dependency type to express requirements of a project. module demo_requirements use demo_error, only : error_type, fatal_error use tomlf, only : toml_table, toml_key, get_value, set_value implicit none private public :: requirement_type, new_requirement, new_requirements !> Declaration of a dependency type :: requirement_type !> Name to identify dependency character(len=:), allocatable :: name ! ... further declaration of entries end type requirement_type contains !> Create a new list of requirements from a table subroutine new_requirements(error, table, req) !> Error handling type(error_type), allocatable, intent(out) :: error !> TOML data structure type(toml_table), intent(inout) :: table !> List of all dependencies type(requirement_type), allocatable, intent(out) :: req(:) integer :: ikey type(toml_key), allocatable :: list(:) type(toml_table), pointer :: child type(toml_table), allocatable, target :: dummy character(len=:), allocatable :: version ! Get all entries of current table, the `list` is guaranteed to be allocated call table%get_keys(list) allocate(req(size(list))) do ikey = 1, size(list) call get_value(table, list(ikey), child) ! Not providing a subtable is okay if a string provides the version constraint if (.not. associated(child)) then call get_value(table, list(ikey), version) if (.not.allocated(version)) then call fatal_error(error, "Requirement '"//list(ikey)%key//& & "' must be version constraint or subtable") exit end if ! Create a dummy table we can reference when initializing dummy = toml_table() call set_value(dummy, "version", version) child => dummy end if ! Initialize dependency from subtable call new_requirement(error, child, req(ikey), list(ikey)%key) ! Cleanup, alternatively the dummy could be pushed back into the main table if (allocated(dummy)) deallocate(dummy) ! Leave loop in case of error if (allocated(error)) exit end do ! Requirements are in a half-finished state, invalidate them to avoid confusion if (allocated(error)) deallocate(req) end subroutine new_requirements !> Initialize a dependency from a TOML data structure subroutine new_requirement(error, table, req, name) !> Error handling type(error_type), allocatable, intent(out) :: error !> TOML data structure type(toml_table), intent(inout) :: table !> New dependency object type(requirement_type), intent(out) :: req !> Name of the dependency character(len=*), intent(in) :: name req%name = name ! ... further reading of entries end subroutine new_requirement end module demo_requirements fortran-toml-0.5.0/doc/how-to/table/keys/src/error.f900000664000175000017500000000233115201541453022607 0ustar alastairalastair!> Central registry for error codes module demo_error implicit none private public :: error_type, fatal_error !> Possible error codes type :: enum_stat !> Successful run integer :: success = 0 !> Internal error: integer :: fatal = 1 end type enum_stat !> Actual enumerator for return states type(enum_stat), parameter :: demo_stat = enum_stat() !> Error message type :: error_type !> Error code integer :: stat !> Payload of the error character(len=:), allocatable :: message end type error_type contains !> A fatal error is encountered subroutine fatal_error(error, message, stat) !> Instance of the error type(error_type), allocatable, intent(out) :: error !> A detailed message describing the error and (optionally) offering advice character(len=*), intent(in), optional :: message !> Overwrite of the error code integer, intent(in), optional :: stat allocate(error) if (present(stat)) then error%stat = stat else error%stat = demo_stat%fatal end if if (present(message)) then error%message = message else error%message = "Fatal error" end if end subroutine fatal_error end module demo_error fortran-toml-0.5.0/doc/how-to/table/keys/fpm.toml0000664000175000017500000000007215201541453022026 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." fortran-toml-0.5.0/doc/how-to/table/aot/0000775000175000017500000000000015201541453020160 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/table/aot/src/0000775000175000017500000000000015201541453020747 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/table/aot/src/task.f900000664000175000017500000000743615201541453022243 0ustar alastairalastair!> Example for a workflow pipeline to construct a molecular dynamics simulation. module demo_task use tomlf, only : toml_table, toml_array, get_value, len implicit none private public :: read_tasks, task_config, driver_config, lbfgs_config, velocity_verlet_config !> Abstract base class for all simulation driver configurations type, abstract :: driver_config end type driver_config !> Configuration for the LBFGS geometry optimization driver type, extends(driver_config) :: lbfgs_config !> Tolerance for considering optimization to be converged real :: tolerance end type lbfgs_config !> Configuration for the Velocity-Verlet molecular dynamics driver type, extends(driver_config) :: velocity_verlet_config !> Time step for the propagation in fs real :: time_step !> Temperature in K real :: temperature !> Number of steps to take in the propagation integer :: max_steps end type velocity_verlet_config !> Configuration of a single simulation task type :: task_config !> Label to identify the task character(len=:), allocatable :: label !> Driver configuration class(driver_config), allocatable :: config end type task_config contains !> Read task configurations from document root subroutine read_tasks(table, task) !> Data structure type(toml_table), intent(inout) :: table !> Configurations for simulation tasks type(task_config), allocatable, intent(out) :: task(:) integer :: itask type(toml_array), pointer :: array type(toml_table), pointer :: child call get_value(table, "tasks", array) allocate(task(len(array))) do itask = 1, size(task) call get_value(array, itask, child) call read_task(child, task(itask)) end do end subroutine read_tasks !> Read a single task configuration subroutine read_task(table, task) !> Data structure type(toml_table), intent(inout) :: table !> Configuration for simulation task type(task_config), intent(out) :: task character(len=:), allocatable :: driver type(toml_table), pointer :: child call get_value(table, "driver", driver) if (.not.allocated(driver)) then error stop "Driver keyword not present in task configuration" end if call get_value(table, "name", task%label, driver) select case(driver) case default error stop "Unknown driver type in task configuration" case("lbfgs") block type(lbfgs_config), allocatable :: tmp allocate(tmp) call get_value(table, "config", child) call read_lbfgs(child, tmp) call move_alloc(tmp, task%config) end block case("velocity-verlet") block type(velocity_verlet_config), allocatable :: tmp allocate(tmp) call get_value(table, "config", child) call read_velocity_verlet(child, tmp) call move_alloc(tmp, task%config) end block end select end subroutine read_task !> Read configuration for LBFGS geometry optimization subroutine read_lbfgs(table, config) !> Data structure type(toml_table), intent(inout) :: table !> Configuration for LBFGS optimizer type(lbfgs_config), intent(out) :: config call get_value(table, "tolerance", config%tolerance, 1.0e-6) end subroutine read_lbfgs !> Read configuration for Velocity-Verlet molecular dynamics subroutine read_velocity_verlet(table, config) !> Data structure type(toml_table), intent(inout) :: table !> Configuration for Velocity-Verlet propagator type(velocity_verlet_config), intent(out) :: config call get_value(table, "time-step", config%time_step, 0.5) call get_value(table, "temperature", config%temperature, 298.15) call get_value(table, "max-steps", config%max_steps, 100) end subroutine read_velocity_verlet end module demo_task fortran-toml-0.5.0/doc/how-to/table/aot/fpm.toml0000664000175000017500000000007215201541453021636 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." fortran-toml-0.5.0/doc/how-to/serde/0000775000175000017500000000000015201541453017410 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/serde/src/0000775000175000017500000000000015201541453020177 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/serde/src/demo.f900000664000175000017500000000400615201541453021443 0ustar alastairalastairmodule demo_serde use serde_class, only : serde_record use serde_error, only : error_type, fatal_error use tomlf, only : toml_table, get_value, set_value implicit none private public :: example_record type, extends(serde_record) :: example_record integer :: nrun real :: alpha character(len=:), allocatable :: label contains !> Read configuration data from TOML data structure procedure :: load_from_toml !> Write configuration data to TOML data structure procedure :: dump_to_toml end type example_record contains !> Read configuration data from TOML data structure subroutine load_from_toml(self, table, error) !> Instance of the configuration data class(example_record), intent(inout) :: self !> Data structure type(toml_table), intent(inout) :: table !> Error handling type(error_type), allocatable, intent(out) :: error integer :: stat call get_value(table, "nrun", self%nrun, 10, stat=stat) if (stat /= 0) then call fatal_error(error, "Invalid entry for number of runs") return end if call get_value(table, "alpha", self%alpha, 1.0, stat=stat) if (stat /= 0) then call fatal_error(error, "Invalid entry for alpha parameter") return end if call get_value(table, "label", self%label, stat=stat) if (stat /= 0) then call fatal_error(error, "Invalid entry for data label") return end if end subroutine load_from_toml !> Write configuration data to TOML datastructure subroutine dump_to_toml(self, table, error) !> Instance of the configuration data class(example_record), intent(in) :: self !> Data structure type(toml_table), intent(inout) :: table !> Error handling type(error_type), allocatable, intent(out) :: error call set_value(table, "nrun", self%nrun) call set_value(table, "alpha", self%alpha) if (allocated(self%label)) then call set_value(table, "label", self%label) end if end subroutine dump_to_toml end module demo_serde fortran-toml-0.5.0/doc/how-to/serde/src/serde_class.f900000664000175000017500000001066415201541453023015 0ustar alastairalastair!> Definition of configuration data with serde properties. !> Each data record knows how to serialize and deserialize itself. module serde_class use serde_error, only : error_type, fatal_error use tomlf, only : toml_table, toml_error, toml_load, toml_dump implicit none private public :: serde_record !> Serializable and deserializable configuration data record type, abstract :: serde_record contains !> Reading of configuration data generic :: load => load_from_file, load_from_unit, load_from_toml !> Read configuration data from file procedure, private :: load_from_file !> Read configuration data from formatted unit procedure, private :: load_from_unit !> Read configuration data from TOML data structure procedure(load_from_toml), deferred :: load_from_toml !> Writing of configuration data generic :: dump => dump_to_file, dump_to_unit, dump_to_toml !> Write configuration data to file procedure, private :: dump_to_file !> Write configuration data to formatted unit procedure, private :: dump_to_unit !> Write configuration data to TOML data structure procedure(dump_to_toml), deferred :: dump_to_toml end type serde_record abstract interface !> Read configuration data from TOML data structure subroutine load_from_toml(self, table, error) import :: serde_record, toml_table, error_type !> Instance of the configuration data class(serde_record), intent(inout) :: self !> Data structure type(toml_table), intent(inout) :: table !> Error handling type(error_type), allocatable, intent(out) :: error end subroutine load_from_toml !> Write configuration data to TOML datastructure subroutine dump_to_toml(self, table, error) import :: serde_record, toml_table, error_type !> Instance of the configuration data class(serde_record), intent(in) :: self !> Data structure type(toml_table), intent(inout) :: table !> Error handling type(error_type), allocatable, intent(out) :: error end subroutine dump_to_toml end interface contains !> Read configuration data from file subroutine load_from_file(self, file, error) !> Instance of the configuration data class(serde_record), intent(inout) :: self !> File name character(len=*), intent(in) :: file !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_error), allocatable :: parse_error type(toml_table), allocatable :: table call toml_load(table, file, error=parse_error) if (allocated(parse_error)) then allocate(error) call move_alloc(parse_error%message, error%message) return end if call self%load(table, error) if (allocated(error)) return end subroutine load_from_file !> Read configuration data from file subroutine load_from_unit(self, unit, error) !> Instance of the configuration data class(serde_record), intent(inout) :: self !> File name integer, intent(in) :: unit !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_error), allocatable :: parse_error type(toml_table), allocatable :: table call toml_load(table, unit, error=parse_error) if (allocated(parse_error)) then allocate(error) call move_alloc(parse_error%message, error%message) return end if call self%load(table, error) if (allocated(error)) return end subroutine load_from_unit !> Write configuration data to file subroutine dump_to_file(self, file, error) !> Instance of the configuration data class(serde_record), intent(in) :: self !> File name character(len=*), intent(in) :: file !> Error handling type(error_type), allocatable, intent(out) :: error integer :: unit open(file=file, newunit=unit) call self%dump(unit, error) close(unit) if (allocated(error)) return end subroutine dump_to_file !> Write configuration data to file subroutine dump_to_unit(self, unit, error) !> Instance of the configuration data class(serde_record), intent(in) :: self !> Formatted unit integer, intent(in) :: unit !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table table = toml_table() call self%dump(table, error) if (.not.allocated(error)) then call toml_dump(table, unit, error) end if end subroutine dump_to_unit end module serde_class fortran-toml-0.5.0/doc/how-to/serde/src/serde_error.f900000664000175000017500000000233515201541453023035 0ustar alastairalastair!> Central registry for error codes module serde_error implicit none private public :: error_type, fatal_error !> Possible error codes type :: enum_stat !> Successful run integer :: success = 0 !> Internal error: integer :: fatal = 1 end type enum_stat !> Actual enumerator for return states type(enum_stat), parameter :: serde_stat = enum_stat() !> Error message type :: error_type !> Error code integer :: stat !> Payload of the error character(len=:), allocatable :: message end type error_type contains !> A fatal error is encountered subroutine fatal_error(error, message, stat) !> Instance of the error type(error_type), allocatable, intent(out) :: error !> A detailed message describing the error and (optionally) offering advice character(len=*), intent(in), optional :: message !> Overwrite of the error code integer, intent(in), optional :: stat allocate(error) if (present(stat)) then error%stat = stat else error%stat = serde_stat%fatal end if if (present(message)) then error%message = message else error%message = "Fatal error" end if end subroutine fatal_error end module serde_error fortran-toml-0.5.0/doc/how-to/serde/fpm.toml0000664000175000017500000000006715201541453021072 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../.." fortran-toml-0.5.0/doc/how-to/datetime/0000775000175000017500000000000015201541453020102 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/datetime/datetime-fortran/0000775000175000017500000000000015201541453023347 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/datetime/datetime-fortran/src/0000775000175000017500000000000015201541453024136 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/datetime/datetime-fortran/src/compat.f900000664000175000017500000002062415201541453025745 0ustar alastairalastair!> Compatibility module to allow working with [[datetime]] objects and TOML data structures module demo_compat use datetime_module, only : datetime use tomlf, only : toml_table, toml_array, toml_keyval, toml_key, toml_stat, toml_datetime, & & get_value, set_value, add_keyval, len implicit none private public :: get_value, set_value public :: assignment(=) !> Getter functions to manipulate TOML values interface get_value module procedure :: get_value_datetime_fortran module procedure :: get_child_value_datetime_fortran module procedure :: get_key_value_datetime_fortran module procedure :: get_elem_value_datetime_fortran end interface get_value !> Setter functions to manipulate TOML values interface set_value module procedure :: set_value_datetime_fortran module procedure :: set_child_value_datetime_fortran module procedure :: set_key_value_datetime_fortran module procedure :: set_elem_value_datetime_fortran end interface set_value !> Define assignment operation between [[toml_datatime]] and [[datetime]] objects interface assignment(=) module procedure :: convert_to_datetime module procedure :: convert_from_datetime end interface assignment(=) contains !> Set TOML value to datetime subroutine set_value_datetime_fortran(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Datetime value type(datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_datetime) :: tmp tmp = val call set_value(self, tmp, stat, origin) end subroutine set_value_datetime_fortran !> Set TOML value to datetime subroutine set_child_value_datetime_fortran(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(*), intent(in) :: key !> Datetime value type(datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_datetime_fortran !> Set TOML value to datetime subroutine set_key_value_datetime_fortran(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Datetime value type(datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_datetime_fortran !> Retrieve TOML value as datetime value subroutine set_elem_value_datetime_fortran(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Datetime value type(datetime), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_datetime_fortran !> Retrieve TOML value as datetime subroutine get_value_datetime_fortran(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Datetime value type(datetime), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_datetime) :: tmp call get_value(self, tmp, stat, origin) val = tmp end subroutine get_value_datetime_fortran !> Retrieve TOML value as datetime subroutine get_child_value_datetime_fortran(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(*), intent(in) :: key !> Datetime value type(datetime), intent(out) :: val !> Default datetime value type(datetime), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if end if end subroutine get_child_value_datetime_fortran !> Retrieve TOML value as datetime subroutine get_key_value_datetime_fortran(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Datetime value type(datetime), intent(out) :: val !> Default datetime value type(datetime), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_datetime_fortran !> Retrieve TOML value as datetime subroutine get_elem_value_datetime_fortran(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value type(datetime), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_datetime_fortran !> Convert to a TOML representation of a datetime object elemental subroutine convert_to_datetime(lhs, rhs) !> TOML representation of datetime type(toml_datetime), intent(out) :: lhs !> Datetime-fortran representation of datetime type(datetime), intent(in) :: rhs lhs%date%year = rhs%getYear() lhs%date%month = rhs%getMonth() lhs%date%day = rhs%getDay() lhs%time%hour = rhs%getHour() lhs%time%minute = rhs%getMinute() lhs%time%second = rhs%getSecond() lhs%time%msec = rhs%getMillisecond() * 1000 end subroutine convert_to_datetime !> Convert from a TOML representation of a datetime object elemental subroutine convert_from_datetime(lhs, rhs) !> Datetime-fortran representation of datetime type(datetime), intent(out) :: lhs !> TOML representation of datetime type(toml_datetime), intent(in) :: rhs integer, allocatable :: year, month, day, hour, minute, second, millisecond if (rhs%date%year > -1) year = rhs%date%year if (rhs%date%month > -1) month = rhs%date%month if (rhs%date%day > -1) day = rhs%date%day if (rhs%time%hour > -1) hour = rhs%time%hour if (rhs%time%minute > -1) minute = rhs%time%minute if (rhs%time%second > -1) second = rhs%time%second if (rhs%time%msec > -1) millisecond = rhs%time%msec / 1000 lhs = datetime(year=year, month=month, day=day, hour=hour, minute=minute, & & second=second, millisecond=millisecond) end subroutine convert_from_datetime end module demo_compat fortran-toml-0.5.0/doc/how-to/datetime/datetime-fortran/fpm.toml0000664000175000017500000000020515201541453025023 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." datetime.git = "https://github.com/wavebitscientific/datetime-fortran.git" fortran-toml-0.5.0/doc/how-to/datetime/M_time/0000775000175000017500000000000015201541453021314 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/datetime/M_time/src/0000775000175000017500000000000015201541453022103 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/datetime/M_time/src/compat.f900000664000175000017500000002024015201541453023704 0ustar alastairalastair!> Compatibility module to allow working with [[date_time]] objects and TOML data structures module demo_compat use m_time_oop, only : date_time use tomlf, only : toml_table, toml_array, toml_keyval, toml_key, toml_stat, toml_datetime, & & get_value, set_value, add_keyval, len implicit none private public :: get_value, set_value public :: assignment(=) !> Getter functions to manipulate TOML values interface get_value module procedure :: get_value_datetime_m_time module procedure :: get_child_value_datetime_m_time module procedure :: get_key_value_datetime_m_time module procedure :: get_elem_value_datetime_m_time end interface get_value !> Setter functions to manipulate TOML values interface set_value module procedure :: set_value_datetime_m_time module procedure :: set_child_value_datetime_m_time module procedure :: set_key_value_datetime_m_time module procedure :: set_elem_value_datetime_m_time end interface set_value !> Define assignment operation between [[toml_datatime]] and M_time [[date_time]] objects interface assignment(=) module procedure :: convert_to_datetime module procedure :: convert_from_datetime end interface assignment(=) contains !> Set TOML value to datetime subroutine set_value_datetime_m_time(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(inout) :: self !> Datetime value type(date_time), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_datetime) :: tmp tmp = val call set_value(self, tmp, stat, origin) end subroutine set_value_datetime_m_time !> Set TOML value to datetime subroutine set_child_value_datetime_m_time(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(*), intent(in) :: key !> Datetime value type(date_time), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, .true., stat, origin) if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) then if (stat == toml_stat%success) stat = toml_stat%fatal end if end if end subroutine set_child_value_datetime_m_time !> Set TOML value to datetime subroutine set_key_value_datetime_m_time(table, key, val, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Datetime value type(date_time), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call set_value(table, key%key, val, stat, origin) end subroutine set_key_value_datetime_m_time !> Retrieve TOML value as datetime value subroutine set_elem_value_datetime_m_time(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Datetime value type(date_time), intent(in) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (.not.associated(ptr)) then if (pos == len(array) + 1) then call add_keyval(array, ptr, stat) end if end if if (associated(ptr)) then call set_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine set_elem_value_datetime_m_time !> Retrieve TOML value as datetime subroutine get_value_datetime_m_time(self, val, stat, origin) !> Instance of the key-value pair class(toml_keyval), intent(in) :: self !> Datetime value type(date_time), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_datetime) :: tmp call get_value(self, tmp, stat, origin) val = tmp end subroutine get_value_datetime_m_time !> Retrieve TOML value as datetime subroutine get_child_value_datetime_m_time(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table character(*), intent(in) :: key !> Datetime value type(date_time), intent(out) :: val !> Default datetime value type(date_time), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(table, key, ptr, present(default), stat, origin) if (associated(ptr)) then if (allocated(ptr%val)) then call get_value(ptr, val, stat, origin) else if (present(default)) then call set_value(ptr, default) call get_value(ptr, val, stat=stat) else if (present(stat)) stat = toml_stat%fatal end if end if end if end subroutine get_child_value_datetime_m_time !> Retrieve TOML value as datetime subroutine get_key_value_datetime_m_time(table, key, val, default, stat, origin) !> Instance of the TOML table class(toml_table), intent(inout) :: table !> Key in this TOML table type(toml_key), intent(in) :: key !> Datetime value type(date_time), intent(out) :: val !> Default datetime value type(date_time), intent(in), optional :: default !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin call get_value(table, key%key, val, default, stat, origin) end subroutine get_key_value_datetime_m_time !> Retrieve TOML value as datetime subroutine get_elem_value_datetime_m_time(array, pos, val, stat, origin) !> Instance of the TOML array class(toml_array), intent(inout) :: array !> Position in the array integer, intent(in) :: pos !> Integer value type(date_time), intent(out) :: val !> Status of operation integer, intent(out), optional :: stat !> Origin in the data structure integer, intent(out), optional :: origin type(toml_keyval), pointer :: ptr call get_value(array, pos, ptr, stat, origin) if (associated(ptr)) then call get_value(ptr, val, stat, origin) else if (present(stat)) stat = toml_stat%fatal end if end subroutine get_elem_value_datetime_m_time !> Convert to a TOML representation of a datetime object elemental subroutine convert_to_datetime(lhs, rhs) !> TOML representation of datetime type(toml_datetime), intent(out) :: lhs !> M_time representation of datetime type(date_time), intent(in) :: rhs lhs%date%year = rhs%year lhs%date%month = rhs%month lhs%date%day = rhs%day lhs%time%hour = rhs%hour lhs%time%minute = rhs%minute lhs%time%second = rhs%second lhs%time%msec = rhs%millisecond * 1000 end subroutine convert_to_datetime !> Convert from a TOML representation of a datetime object elemental subroutine convert_from_datetime(lhs, rhs) !> M_time representation of datetime type(date_time), intent(out) :: lhs !> TOML representation of datetime type(toml_datetime), intent(in) :: rhs if (rhs%date%year > -1) lhs%year = rhs%date%year if (rhs%date%month > -1) lhs%month = rhs%date%month if (rhs%date%day > -1) lhs%day = rhs%date%day if (rhs%time%hour > -1) lhs%hour = rhs%time%hour if (rhs%time%minute > -1) lhs%minute = rhs%time%minute if (rhs%time%second > -1) lhs%second = rhs%time%second if (rhs%time%msec > -1) lhs%millisecond = rhs%time%msec / 1000 end subroutine convert_from_datetime end module demo_compat fortran-toml-0.5.0/doc/how-to/datetime/M_time/fpm.toml0000664000175000017500000000016115201541453022771 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." M_time.git = "https://github.com/urbanjost/M_time.git" fortran-toml-0.5.0/doc/how-to/installation.rst0000664000175000017500000002501615201541453021545 0ustar alastairalastair.. _installation: Installing TOML Fortran ======================= This guide will walk you through installing the latest version of TOML Fortran. If you know your way around fpm, CMake or meson, checkout the :ref:`integration guide ` to allow on-demand compilation of TOML Fortran as well as discovery of installed libraries. :fab:`apple` :fab:`linux` :fab:`windows` Installing from conda-forge -------------------------------------------------------------------- .. image:: https://img.shields.io/conda/vn/conda-forge/toml-f :alt: Conda :target: https://github.com/conda-forge/toml-f-feedstock .. image:: https://img.shields.io/conda/pn/conda-forge/toml-f :alt: Conda :target: https://github.com/conda-forge/toml-f-feedstock This project is packaged for the *mamba* package manager and available on the *conda-forge* channel. To install the *mamba* package manager we recommend the `mambaforge `_ installer. If the *conda-forge* channel is not yet enabled, add it to your channels with .. code-block:: bash mamba config --add channels conda-forge mamba config --set channel_priority strict Once the *conda-forge* channel has been enabled, TOML Fortran can be installed with *mamba*: .. code-block:: shell mamba install toml-f It is possible to list all of the versions of TOML Fortran available on your platform with *mamba*: .. code-block:: shell mamba repoquery search toml-f --channel conda-forge :fab:`linux` Debian ------------------- TOML Fortran is available in Debian as `libfortran-toml-dev `_. To install on Debian forky (or later versions), use: .. code-block:: bash sudo apt install libfortran-toml-dev This package includes: - TOML Fortran library files for linking with your Fortran projects - Module files for use in your Fortran code - pkg-config support for easy integration with build systems Once installed, you can use TOML Fortran in your Fortran projects by adding the appropriate module use statements and linking against the library. For build system integration examples, see the :ref:`integration guide `. :fab:`freebsd` FreeBSD ports ---------------------------- .. image:: https://repology.org/badge/version-for-repo/freebsd/toml-f.svg :alt: FreeBSD :target: https://www.freshports.org/textproc/toml-f/ A port for FreeBSD is available .. code-block:: bash pkg install textproc/toml-f In case no package is available build the port using .. code-block:: bash cd /usr/ports/textproc/toml-f make install clean For more information see the `toml-f port details `_. :fab:`linux` Arch Linux User Repository ------------------------------------------- .. image:: https://repology.org/badge/version-for-repo/aur/toml-f.svg :alt: AUR :target: https://aur.archlinux.org/packages/toml-f TOML Fortran is available in the `Arch User Repository (AUR) `_. Install it with an AUR helper like *yay*: .. code-block:: bash yay -S toml-f or build it manually from the AUR .. code-block:: bash git clone https://aur.archlinux.org/toml-f.git cd toml-f makepkg -si :fab:`apple` :fab:`linux` Building with spack --------------------------------------------- .. image:: https://repology.org/badge/version-for-repo/spack/toml-f.svg :alt: Spack :target: https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/toml-f/package.py This project is available with the `spack `_ package manager. You can install the TOML Fortran package with .. code-block:: text spack install toml-f spack load toml-f To depend on TOML Fortran in your `spack`_ package you can add a dependency with .. code-block:: python depends_on("toml-f") :fab:`linux` Using EasyBuild ---------------------------- TOML Fortran is available with `EasyBuild `_. You can check the available configurations using the search option .. code-block:: text ❯ eb -S TOML-Fortran CFGS1=.../easyconfigs/t/TOML-Fortran * $CFGS1/TOML-Fortran-0.2.2-GCC-10.2.0.eb * $CFGS1/TOML-Fortran-0.2.2-iccifort-2020.4.304.eb Select one matching configuration and build TOML Fortran using .. code-block:: text ❯ eb TOML-Fortran-0.2.2-GCC-10.2.0.eb -r == temporary log file in case of crash /tmp/eb-51Jk58/easybuild-NI5Ee1.log == resolving dependencies ... == processing EasyBuild easyconfig TOML-Fortran-0.2.2-GCC-10.2.0.eb == building and installing TOML-Fortran-0.2.2-GCC-10.2.0.eb... [...] == Build succeeded for 1 out of 1 == Temporary log file(s) /tmp/eb-51Jk58/easybuild-NI5Ee1.log* have been removed. == Temporary directory /tmp/eb-51Jk58 has been removed. TOML Fortran should now be available via an environment module. For more details checkout the `EasyBuild`_ documentation. :fab:`apple` Using Homebrew --------------------------- TOML Fortran is available in a custom tap at `grimme-lab/homebrew-qc `__ for the `brew `_ package manager. You can include the tap by using .. code-block:: text brew tap grimme-lab/qc brew install toml-f To build with a custom Fortran compiler you can set the ``FC`` environment variable and force to ignore preexisting binary distributions from the tap .. code-block:: text export FC=gfortran-11 brew install -s toml-f .. note:: TOML Fortran has not yet been submitted to `homebrew-core `_. Building from source -------------------- To build this project from the source code in this repository you need to have - a Fortran compiler supporting Fortran 2008 - GFortran 5 or newer - Intel Fortran 18 or newer - NAG 7 or newer - One of the supported build systems - `meson `_ version 0.55 or newer - `CMake `_ version 3.9 or newer First, get the source by cloning the repository .. code-block:: bash git clone https://github.com/toml-f/toml-f cd toml-f Using Meson ^^^^^^^^^^^ To build this project with meson a build-system backend is required, *i.e.* `ninja `_ version 1.7 or newer. Setup a build with .. code-block:: bash meson setup _build --prefix=/path/to/installation You can select the Fortran compiler by the ``FC`` environment variable. To compile the project run .. code-block:: bash meson compile -C _build We employ a `validator suite `_ to test the standard compliance of this implementation. To use this testing a *go* installation is required. The installation of the validator suite will be handled by meson automatically without installing into the users *go* workspace. Run the tests with .. code-block:: bash meson test -C _build --print-errorlogs To run the full decoder test add the benchmark argument. This test will currently fail, due to the implementation not yet supporting Unicode escape sequences. .. code-block:: bash meson test -C _build --benchmark --print-errorlogs The binary used for transcribing the TOML documents to the testing format is ``_build/test/toml2json`` and can be used to check on per test basis. Finally, you can install TOML Fortran with .. code-block:: bash meson install -C _build Using CMake ^^^^^^^^^^^ While meson is the preferred way to build this project it also offers CMake support. Configure the CMake build with .. code-block:: bash cmake -B_build -GNinja -DCMAKE_INSTALL_PREFIX=/path/to/installation Similar to meson the compiler can be selected with the ``FC`` environment variable. You can build the project using .. code-block:: bash cmake --build _build To include *toml-f* in your CMake project, check the [example integration with CMake](https://github.com/toml-f/tf-cmake-example). The validation suite is currently not supported as unit test for CMake builds and requires a manual setup instead using the *toml2json* binary. Finally, you can install TOML Fortran with .. code-block:: bash cmake --install _build Supported compilers ------------------- This is a non-comprehensive list of tested compilers for TOML Fortran. Compilers with the label *latest* are tested with continuous integration for each commit. ========== =========================== ==================== ============== =============== Compiler Version Platform Architecture version ========== =========================== ==================== ============== =============== GCC 11.1, 10.3, 9.4, 8.5, 7.5 Ubuntu 20.04 x86_64 0.2.3, latest GCC 9.4, 6.5 MacOS 10.15.7 x86_64 0.2.3, latest GCC 11.0 MacOS 11.0 arm64 0.2.3 GCC 9.4 CentOS 7 ppc64le 0.2.3 GCC 9.4 CentOS 7 aarch64 0.2.3 GCC/MinGW 8.1 Window Server 2019 x86_64 0.2.3, latest GCC/MinGW 5.3 Window Server 2019 x86_64 0.2.3 Intel 2022.0 Ubuntu 20.04 x86_64 0.2.3, latest Intel 19 OpenSUSE x86_64 0.2.3 NAG 7.1 RHEL x86_64 0.2.3 ========== =========================== ==================== ============== =============== Compiler known to fail are documented here, together with the last commit where this behaviour was encountered. If available an issue in on the projects issue tracker or the issue tracker of the dependencies is linked. Usually, it safe to assume that older versions of the same compiler will fail to compile as well and this failure is consistent over platforms and/or architectures. ========== ============= =============== ============== ========================== Compiler Version Platform Architecture Reference ========== ============= =============== ============== ========================== Flang 20190329 Ubuntu 20.04 x86_64 `f066ec6`_, `toml-f#28`_ NVHPC 20.9 Manjaro Linux x86_64 `f066ec6`_, `toml-f#27`_ ========== ============= =============== ============== ========================== .. _f066ec6: https://github.com/toml-f/toml-f/tree/f066ec6e7fb96d8faf83ab6614ee664a26ad8d57 .. _toml-f#28: https://github.com/toml-f/toml-f/issues/28 .. _toml-f#27: https://github.com/toml-f/toml-f/issues/27 fortran-toml-0.5.0/doc/how-to/array/0000775000175000017500000000000015201541453017424 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/array/whole/0000775000175000017500000000000015201541453020542 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/array/whole/app/0000775000175000017500000000000015201541453021322 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/array/whole/app/main.f900000664000175000017500000000134415201541453022570 0ustar alastairalastairprogram array use tomlf, only : toml_table, toml_array, get_value, & & toml_parse, toml_error, len implicit none integer, dimension(:), allocatable :: arr_data integer :: data_len, io, i type(toml_table), allocatable :: table type(toml_array), pointer :: arr type(toml_error), allocatable :: parse_error open(newunit=io, file="array.toml") call toml_parse(table, io, parse_error) close(unit=io) if (allocated(parse_error)) then print*, "Error parsing table:"//parse_error%message end if call get_value(table, "data", arr) if (associated(arr)) then call get_value(arr, arr_data) end if print*, "data = ", arr_data end program fortran-toml-0.5.0/doc/how-to/array/whole/fpm.toml0000664000175000017500000000007215201541453022220 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." fortran-toml-0.5.0/doc/how-to/array/nested/0000775000175000017500000000000015201541453020706 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/array/nested/app/0000775000175000017500000000000015201541453021466 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/array/nested/app/nested_array.f900000664000175000017500000000240415201541453024466 0ustar alastairalastairprogram array use tomlf, only : toml_table, toml_array, get_value, & & toml_parse, toml_error, len implicit none integer :: top_len, nested_len, io, i, j, elem type(toml_table), allocatable :: table type(toml_array), pointer :: top_array, nested_array type(toml_error), allocatable :: parse_error open(newunit=io, file="nested_array.toml") call toml_parse(table, io, parse_error) close(unit=io) if (allocated(parse_error)) then print*, "Error parsing table:"//parse_error%message end if ! Get the top-level array and loop over its elements. Each element is an ! array of variable size call get_value(table, "data", top_array) if (associated(top_array)) then top_len = len(top_array) do i = 1,top_len ! Now get a pointer to the i'th nested array call get_value(top_array, i, nested_array) if (associated(nested_array)) then nested_len = len(nested_array) ! And get the elements of the nested array do j = 1,nested_len call get_value(nested_array, j, elem) ! Do something with "elem" here write(*,'(A5,i1,A1,i1,A3,i2)') "data(",i,",",j,") = ",elem end do end if end do end if end program fortran-toml-0.5.0/doc/how-to/array/nested/nested_array.toml0000664000175000017500000000003315201541453024257 0ustar alastairalastairdata=[[1,2,3], [4,5], [6]] fortran-toml-0.5.0/doc/how-to/array/nested/fpm.toml0000664000175000017500000000017215201541453022365 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." [[executable]] name = "mested-array" main = "nested_array.f90" fortran-toml-0.5.0/doc/how-to/array/array/0000775000175000017500000000000015201541453020542 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/array/array/app/0000775000175000017500000000000015201541453021322 5ustar alastairalastairfortran-toml-0.5.0/doc/how-to/array/array/app/parse_array.f900000664000175000017500000000163515201541453024157 0ustar alastairalastairprogram array use tomlf, only : toml_table, toml_array, get_value, & & toml_parse, toml_error, len implicit none integer, dimension(:), allocatable :: arr_data integer :: data_len, io, i type(toml_table), allocatable :: table type(toml_array), pointer :: arr type(toml_error), allocatable :: parse_error open(newunit=io, file="array.toml") call toml_parse(table, io, parse_error) close(unit=io) if (allocated(parse_error)) then print*, "Error parsing table:"//parse_error%message end if ! Get the value of "data" by parsing the array and then looping over its ! elements call get_value(table, "data", arr) if (associated(arr)) then data_len = len(arr) allocate(arr_data(data_len)) do i = 1,data_len call get_value(arr, i, arr_data(i)) end do end if print*, "data = ", arr_data end program fortran-toml-0.5.0/doc/how-to/array/array/fpm.toml0000664000175000017500000000017015201541453022217 0ustar alastairalastairname = "demo" [dependencies] toml-f.path = "../../../.." [[executable]] name = "parse-array" main = "parse_array.f90" fortran-toml-0.5.0/doc/how-to/array/array/array.toml0000664000175000017500000000000015201541453022543 0ustar alastairalastairfortran-toml-0.5.0/doc/how-to/integration.rst0000664000175000017500000002202415201541453021363 0ustar alastairalastair.. _integration: Using TOML Fortran ================== This tutorial shows how to integrate the TOML Fortran library with your build system and use it easily in your project. Using the Fortran package manager --------------------------------- The Fortran package manager (`fpm `_) is a tool for building Fortran projects and managing dependencies on Fortran libraries. To enable TOML Fortran in your fpm project add the following entry to your package manifest: .. code-block:: toml :caption: fpm.toml [dependencies] toml-f.git = "https://github.com/toml-f/toml-f" When building your project fpm will automatically fetch TOML Fortran for you and build it as part of your project. The TOML Fortran modules become useable in your project. Integrate with meson -------------------- To allow meson to use TOML Fortran it is easiest to include it as subproject using a git wrap file placed in the ``subprojects`` directory. .. code-block:: ini :caption: subprojects/toml-f.wrap [wrap-git] directory = toml-f url = https://github.com/toml-f/toml-f revision = head The revision can be adjusted to pin a specific release tag of TOML Fortran for additional stability. In the projects meson buid file the dependency method can be used to access TOML Fortran using the wrap file to define a fallback which is built on-demand. .. code-block:: meson :caption: meson.build # Create TOML Fortran as subproject tomlf_dep = dependency( 'toml-f', version: '>=0.2.0', fallback: ['toml-f', 'tomlf_dep'], default_options: ['default_library=static'], ) Finally, you can add ``tomlf_dep`` as dependency to any of your targets and are done. Integrate with CMake -------------------- To use TOML Fortran in CMake based projects it is useful to define your own find-module, to allow on-demand compilation of TOML Fortran as well as discovery of installed packages from both meson and CMake based builds. .. code-block:: text . ├── CMakeLists.txt ├── cmake │   └── Findtoml-f.cmake : In your main CMake build file you have to include the custom find-module in your ``CMAKE_MODULE_PATH``, afterwards you can just use ``find_package`` to obtain the ``toml-f::toml-f`` target and link against it. .. code-block:: cmake :caption: CMakeLists.txt # ... # Make custom find modules available for project list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) # Find TOML Fortran, check whether target is already available, # e.g. when build as subproject of another project also using TOML Fortran if(NOT TARGET "toml-f::toml-f") find_package(toml-f REQUIRED) endif() # ... # Add library target library( "${PROJECT_NAME}" ) # Link against TOML Fortran library target_link_libraries( "${PROJECT_NAME}" PUBLIC "toml-f::toml-f" ) # ... # Follow GNU conventions for installation destinations include(GNUInstallDirs) # Make custom find-modules available when installing project install( DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) Note that we also install the find-modules, this is important if you want to make your CMake projects reusable in the same way TOML Fortran can be used in your project. Finally we need some boilerplate to define the custom find-module is documented below. Imported Targets ^^^^^^^^^^^^^^^^ This module provides the following imported target, if found: ``toml-f::toml-f`` The toml-f library Result Variables ^^^^^^^^^^^^^^^^ This module will define the following variables: ``TOML_FORTRAN_FOUND`` True if the toml-f library is available ``TOML_FORTRAN_SOURCE_DIR`` Path to the source directory of the toml-f project, only set if the project is included as source. ``TOML_FORTRAN_BINARY_DIR`` Path to the binary directory of the toml-f project, only set if the project is included as source. Cache variables ^^^^^^^^^^^^^^^ The following cache variables may be set to influence the library detection: ``TOML_FORTRAN_FIND_METHOD`` Methods to find or make the project available. Available methods are - ``cmake``: Try to find via CMake config file - ``pkgconf``: Try to find via pkg-config file - ``subproject``: Use source in subprojects directory - ``fetch``: Fetch the source from upstream ``TOML_FORTRAN_DIR`` Used for searching the CMake config file ``TOML_FORTRAN_SUBPROJECT`` Directory to find the toml-f subproject, relative to the project root .. code-block:: cmake :caption: cmake/Findtoml-f.cmake set(_lib "toml-f") set(_pkg "TOML_FORTRAN") set(_url "https://github.com/toml-f/toml-f") set(_rev "HEAD") if(NOT DEFINED "${_pkg}_FIND_METHOD") if(DEFINED "${PROJECT_NAME}-dependency-method") set("${_pkg}_FIND_METHOD" "${${PROJECT_NAME}-dependency-method}") else() set("${_pkg}_FIND_METHOD" "cmake" "pkgconf" "subproject" "fetch") endif() set("_${_pkg}_FIND_METHOD") endif() foreach(method ${${_pkg}_FIND_METHOD}) if(TARGET "${_lib}::${_lib}") break() endif() if("${method}" STREQUAL "cmake") message(STATUS "${_lib}: Find installed package") if(DEFINED "${_pkg}_DIR") set("_${_pkg}_DIR") set("${_lib}_DIR" "${_pkg}_DIR") endif() find_package("${_lib}" CONFIG QUIET) if("${_lib}_FOUND") message(STATUS "${_lib}: Found installed package") break() endif() endif() if("${method}" STREQUAL "pkgconf") find_package(PkgConfig QUIET) pkg_check_modules("${_pkg}" QUIET "${_lib}") if("${_pkg}_FOUND") message(STATUS "Found ${_lib} via pkg-config") add_library("${_lib}::${_lib}" INTERFACE IMPORTED) target_link_libraries( "${_lib}::${_lib}" INTERFACE "${${_pkg}_LINK_LIBRARIES}" ) target_include_directories( "${_lib}::${_lib}" INTERFACE "${${_pkg}_INCLUDE_DIRS}" ) break() endif() endif() if("${method}" STREQUAL "subproject") if(NOT DEFINED "${_pkg}_SUBPROJECT") set("_${_pkg}_SUBPROJECT") set("${_pkg}_SUBPROJECT" "subprojects/${_lib}") endif() set("${_pkg}_SOURCE_DIR" "${PROJECT_SOURCE_DIR}/${${_pkg}_SUBPROJECT}") set("${_pkg}_BINARY_DIR" "${PROJECT_BINARY_DIR}/${${_pkg}_SUBPROJECT}") if(EXISTS "${${_pkg}_SOURCE_DIR}/CMakeLists.txt") message(STATUS "Include ${_lib} from ${${_pkg}_SUBPROJECT}") add_subdirectory( "${${_pkg}_SOURCE_DIR}" "${${_pkg}_BINARY_DIR}" ) add_library("${_lib}::${_lib}" INTERFACE IMPORTED) target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") break() endif() endif() if("${method}" STREQUAL "fetch") message(STATUS "Retrieving ${_lib} from ${_url}") include(FetchContent) FetchContent_Declare( "${_lib}" GIT_REPOSITORY "${_url}" GIT_TAG "${_rev}" ) FetchContent_MakeAvailable("${_lib}") add_library("${_lib}::${_lib}" INTERFACE IMPORTED) target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") FetchContent_GetProperties("${_lib}" SOURCE_DIR "${_pkg}_SOURCE_DIR") FetchContent_GetProperties("${_lib}" BINARY_DIR "${_pkg}_BINARY_DIR") break() endif() endforeach() if(TARGET "${_lib}::${_lib}") set("${_pkg}_FOUND" TRUE) else() set("${_pkg}_FOUND" FALSE) endif() if(DEFINED "_${_pkg}_SUBPROJECT") unset("${_pkg}_SUBPROJECT") unset("_${_pkg}_SUBPROJECT") endif() if(DEFINED "_${_pkg}_DIR") unset("${_lib}_DIR") unset("_${_pkg}_DIR") endif() if(DEFINED "_${_pkg}_FIND_METHOD") unset("${_pkg}_FIND_METHOD") unset("_${_pkg}_FIND_METHOD") endif() unset(_lib) unset(_pkg) unset(_url) unset(_rev) Other build systems ------------------- Other build systems must discover a precompiled TOML Fortran library from the system. For this purpose the ``pkg-config`` tool is used. After installing TOML Fortran with either meson or CMake a pc-file is generated which can be discovered by ``pkg-config`` and describes how to compile against the installed module files as well as link against the TOML Fortran library. First check if the ``pkg-config`` tool is available and can discover TOML Fortran .. code-block:: text pkg-config --modversion toml-f Make sure to adjust the ``PKG_CONFIG_PATH`` environment variable to point to the correct installation directory. Using the ``--libs`` and ``--cflags`` options the libraries to link against as well as the include directories can be obtained: .. code-block:: text pkg-config --cflags toml-f pkg-config --libs toml-f In a handwritten Makefile those can be included by .. code-block:: make TOML_FORTRAN_INCLUDE_FLAGS := $(shell pkg-config --cflags toml-f) TOML_FORTRAN_LIBRARY_FLAGS := $(shell pkg-config --libs toml-f) fortran-toml-0.5.0/doc/how-to/table.rst0000664000175000017500000004004315201541453020130 0ustar alastairalastairWorking with tables =================== The central data structures in TOML are tables, they contain a map from a key (string) to any supported data type in TOML. These recipes describe common scenarios for retrieving data from tables using the TOML Fortran library. Accessing tables ---------------- When accessing tables there are two main modes, first the default mode which implicitly creates a table if it does not exist yet and second the mode where no tables will be inserted. The first mode is useful when the presence of a table is optional, for example when reading configuration files where certain features can be activated by providing a subtable with the respective settings. The second mode is useful when certain tables are mandatory for the program to work correctly. .. code-block:: Fortran block use tomlf, only : toml_table, get_value type(toml_table) :: table type(toml_table), pointer :: child table = toml_table() ! create an empty table call get_value(table, "optional", child) ! child pointer is associated, since "optional" table is created implicitly print *, associated(child) ! expect T call get_value(table, "mandatory", child, requested=.false.) ! child pointer is not associated, since "mandatory" table does not exist print *, associated(child) ! expect F end block To better distinguish between different results of accessing a table, the status of the operation can be requested as well. This is useful if the value is present, but the value is not a table. .. code-block:: Fortran block use tomlf, only : toml_table, get_value, set_value, toml_stat type(toml_table) :: table type(toml_table), pointer :: child integer :: stat table = toml_table() ! create an empty table call get_value(table, "optional", child, stat=stat) ! child pointer is associated, since "optional" table is created implicitly print *, associated(child) ! expect T ! status indicates success since the table was created print *, stat == toml_stat%success ! expect T call get_value(table, "mandatory", child, requested=.false., stat=stat) ! child pointer is not associated, since "mandatory" table does not exist print *, associated(child) ! expect F ! status indicates success, since the key is not requested print *, stat == toml_stat%success ! expect T call set_value(table, "not-a-table", "some string") ! set a string value call get_value(table, "not-a-table", child, stat=stat) ! child pointer is not associated, since "not-a-table" entry is not a table print *, associated(child) ! expect F ! type mismatch is reported in the status print *, stat == toml_stat%type_mismatch ! expect T call get_value(table, "not-a-table", child, requested=.false., stat=stat) ! child pointer is not associated, since "not-a-table" entry is not a table print *, associated(child) ! expect F ! type mismatch is reported in the status print *, stat == toml_stat%type_mismatch ! expect T end block If you need absence of a table to be handled, the association status of the returned pointer should be used to check whether the table was present or not. Accessing nested tables ----------------------- Using nested tables provides the possibility to better group configuration data. Since the TOML format always requires the full qualified path in each table header, it is easy for the user to identify where the current settings belong to. On the other hand, deeply nested tables with long table paths or path components make them more difficult to use and a good balance of short and expressive table names and meaningful subtables is required. An example of an electronic structure code implementing different Hamiltonians is given below. .. code-block:: toml [hamiltonian.dftb] scc = {} skf.format = "mio-1-1/{}-{}.skf" [analysis] calculate-forces = true The deepest nested subtable with entries in this example is the *hamiltonian.dftb.skf* path. Such layout in the configuration file will usually be mirrored in the actual implementation, with every table corresponding to a derived type describing the input. For the example above in total six derived types for the individual tables are defined as .. literalinclude:: table/nested/src/input.f90 :caption: src/input.f90 :language: Fortran :lines: 10-54 .. note:: The representation in Fortran derived types looks lengthy compared to the actual TOML input. Consider that the 40 lines of Fortran code contain 50% comments describing the data types briefly for (future) developers. Of course, the user documentation of the input format will be much more extensive, containing descriptions for every table and every entry, including input ranges and unit conventions. The final input file provided by the user can be brief and expressive. Staring with the root of the table which is read in the *simulation_input* there are two ways to obtain access to a subtable, first we get the *hamiltonian* subtable, which we defined as mandatory, using the ``get_value`` interface. In case it is present a reference will be returned in the *child* pointer. If no table is available in the input TOML Fortran will insert it into the root table and return the reference to the newly created table. The *child* pointer can still be unassigned in case invalid input is provided, which will result in raising an error in the implementation shown below. The alternative is to explicitly mark the subtable as optional, like for the *analysis* table, if no table is available or the entry is invalid the *child* pointer will not be assigned. To differentiate those cases we can request the status information, check whether the operation was successful, and cleanly handle the error case. .. literalinclude:: table/nested/src/input.f90 :caption: src/input.f90 :language: Fortran :lines: 58-90 The same happens for reading the *hamiltonian_input* and *dftb_input* entry. .. literalinclude:: table/nested/src/input.f90 :caption: src/input.f90 :language: Fortran :lines: 92-140 Finally, we can implement reading the terminal subtables into the *scc_input*, *skf_input*, and *analysis_input*, where we retrieve the actual values using the ``get_value`` interface. Note that we can conveniently define default values using the ``get_value`` interface. For proper error handling, we can retrieve the optional *stat* argument as well. .. literalinclude:: table/nested/src/input.f90 :caption: src/input.f90 :language: Fortran :lines: 142-179 For the small incomplete input as shown here, the fine-grained substructure seems overengineered and could be fully defined in the reading routine for the document root as well. However, for larger program inputs such a structure can help to ensure that input readers are properly modular and reusable. .. tip:: The allocation status of a component of the derived type can be used instead of a separate boolean flag to indicate whether a feature should be activated. This avoids requiring conditional code inside a reader routine for conditionally handling entries depending on a boolean flag, instead they can be collected in a subtable. .. dropdown:: Full source code The full module implementing the *simulation_input* reading .. literalinclude:: table/nested/src/input.f90 :caption: src/input.f90 :language: fortran The auxiliary module providing the error handler .. literalinclude:: table/nested/src/error.f90 :caption: src/error.f90 :language: fortran Direct access via key paths --------------------------- If only a deeply nested value of a data structure is needed it can be retrieved by using a key path. The build interface will internally walk the key path, resolve the child tables and create them as necessary. .. warning:: Repeatly accessing values via a key path from the document root, rather than retrieving the reference the desired child table, will introduce an overhead each time the key path is resolved. For the previous example we can use the key path access to retrieve the most deeply nested value as shown below. .. code-block:: fortran block use tomlf, only : get_value, toml_path, toml_key character(:), allocatable :: format_string call get_value(table, toml_path("hamiltonian", "dftb", "skf", "format"), format_string) end block Similar like other build interfaces it can be used to create the subtables as well as the string value by providing a default. Iterating over keys ------------------- An expressive way to organize data is by providing a table where the keys of each entry describe the object that should be initialized. For example in a package manager, the keys represent the dependency, where each dependency is declared in a subtable. Furthermore, a convenience feature might be the possibility to just provide a string, which is interpreted as a version subentry. The final usage of this in a *requirements* table could look like the snippet shown below. .. code-block:: toml [requirements] stdlib = "^0.2.1" toml = "^1.0.0" cli2 = "^3.1.0" lapack.version = "^3.10.1" lapack.variant = "mkl|openblas" minpack = {git="https://github.com/fortran-lang/minpack@v2.0.0"} The first three entries provide a string value, while the fourth entry provides a subtable implicitly by using dotted key-value pairs and the last entry uses an inline table. Here we want to focus on the iteration and the default initialization, the internal structure of the *requirement_type* is secondary for this example. We provide the minimal definition only holding the name of the dependency for demonstration purposes. .. literalinclude:: table/keys/src/requirements.f90 :caption: src/requirements.f90 :language: fortran :lines: 10-15 For the actual implementation of reading all entries from the table, we will use a one-dimensional array of *requirement_type* values. Using the ``get_keys`` method of the table we can obtain a list of all keys for the current table, the method will always allocate the ``list`` variable and we can safely allocate the *requirement_type* using the number of keys. To obtain the subtable, the ``get_value`` interface can be used, it will return a pointer to the subtable, either created implicitly by using a dotted key-value pair or by an inline table as shown in the snippet above. Finally, we can call the actual constructor of the *requirement_type* using the subtable references with the ``child`` pointer. .. literalinclude:: table/keys/src/requirements.f90 :caption: src/requirements.f90 :language: fortran :lines: 19-66 The other scenario we want to support is the presence of a string rather than a subtable. In this case, the ``get_value`` interface will fail, while it provides an optional status argument to check for successful operation, we can more conveniently and idiomatically verify the success by checking the state of the ``child`` pointer. If there is no subtable to reference, *i.e.* because it is a key-value pair with a string entry, the ``child`` pointer will not be associated, which can be easily checked. For this case we will again use the ``get_value`` interface, but this time to retrieve the entry into a deferred length character. Again we can idiomatically check the status of the operation using the allocation state of the variable and create the appropriate error message if needed. Eventually, we have to provide the constructor of the requirements with a table, for this purpose we create a dummy table and set the entry at the version key to the just retrieved string. The newly created dummy table can be associated with the ``child`` pointer and passed to the actual constructor. The actual constructor for our example is very minimalistic and only recovers the name of the dependency which is passed as a separate argument. .. literalinclude:: table/keys/src/requirements.f90 :caption: src/requirements.f90 :language: fortran :lines: 68-81 .. note:: While we provide an error handler in the example, we also ensure that the allocation status of the *requirement_type* values communicates the status of the operation as well. .. dropdown:: Full source code The full module implementing the *requirement_type* reading .. literalinclude:: table/keys/src/requirements.f90 :caption: src/requirements.f90 :language: fortran The auxiliary module providing the error handler .. literalinclude:: table/keys/src/error.f90 :caption: src/error.f90 :language: fortran Array of tables --------------- A special construct in TOML is the array of tables syntax, it provides a more verbose form to declare several tables in an array, which are usually provided using inline tables as shown below. .. code-block:: toml tasks = [ {name="optimization", driver="lbfgs"}, {name="equilibration", driver="velocity-verlet"}, {name="production", driver="velocity-verlet"}, ] Comparing the above example to the snippet below using an array of tables for the *tasks* array, the more verbose form becomes preferable in case further subtables are needed. Except for the subtables *config* the same data is provided. .. code-block:: toml [[tasks]] name = "optimization" driver = "lbfgs" [task.config] tolerance = 1.0e-7 [[tasks]] name = "equilibration" driver = "velocity-verlet" [task.config] time-step = 1.0 temperature = 300.0 max-steps = 500 [[tasks]] name = "production" driver = "velocity-verlet" [task.config] time-step = 1.0 temperature = 300.0 max-steps = 10000 To represent this data we can use a single *task_config* derived type with a polymorphic *driver_config* member identifying the actual task. For this example, we will have two implementations of such tasks such as LBFGS and Velocity Verlet, which are defined in the following snippets. .. literalinclude:: table/aot/src/task.f90 :caption: src/task.f90 :language: fortran :lines: 9-35 To read the array of tables we start from the root document and fetch the *tasks* entry as an array using the ``get_value`` interface. The length of the full arrays is known and we can use it to allocate the list of *task_config* values before reading the individual entries. The individual tables inside the array can be addressed using the ``get_value`` interface by passing the (one-based) index. .. literalinclude:: table/aot/src/task.f90 :caption: src/task.f90 :language: fortran :lines: 39-57 .. note:: In the setup above, if the *tasks* entry is not present it will be implicitly created as an empty array. The allocation and the loop over the entries will work, however the consuming code should check whether no tasks are meaningful or should produce an error. To read the individual tasks we define a separate procedure to make it easily reusable and hide the fact that we are working with a subtable. To make the task *name* optional we make it default to the driver name, for *allocatable* or *pointer* variables the exit status of ``get_value`` can be easily checked by the allocation or association status of the respective variable, alternatively an integer variable can be passed to the optional *stat* argument. Finally, the configuration reader is called depending on the value of *driver* for ease of usage we use a block construct to allocate the specific type and then transfer it using *move_alloc* into the *task_config*. .. literalinclude:: table/aot/src/task.f90 :caption: src/task.f90 :language: fortran :lines: 59-95 For reading the actual driver configuration we use the ``get_value`` interface to obtain the settings. We use the same defaulting mechanism as for the *name* entry here. .. literalinclude:: table/aot/src/task.f90 :caption: src/task.f90 :language: fortran :lines: 97-117 Note that this example does not propagate back errors but directly calls *error stop*, for a more robust error reporting this can be changed by a small error handle or a context type. .. dropdown:: Full source code The full module implementing the *task_config* reading .. literalinclude:: table/aot/src/task.f90 :caption: src/task.f90 :language: fortran fortran-toml-0.5.0/doc/.gitignore0000664000175000017500000000003215201541453017054 0ustar alastairalastair*.mo _build/ __pycache__/ fortran-toml-0.5.0/doc/external.rst0000664000175000017500000000064015201541453017445 0ustar alastairalastair.. _external: External resources ================== Sites and blog posts with information on TOML Fortran are mentioned and linked here. .. toctree:: Modern Fortran by Philipp Engel (English) Fortran in Action by Zuo Zhihua (Chinese) fortran-toml-0.5.0/doc/_templates/0000775000175000017500000000000015201541453017226 5ustar alastairalastairfortran-toml-0.5.0/doc/_templates/layout.html0000664000175000017500000000045015201541453021430 0ustar alastairalastair{% extends "!layout.html" %} {% block extrahead %} {%- endblock %} fortran-toml-0.5.0/assets/0000775000175000017500000000000015201541453015626 5ustar alastairalastairfortran-toml-0.5.0/assets/toml-f-32x32.png0000664000175000017500000000142515201541453020313 0ustar alastairalastairPNG  IHDR szz pHYsyy ,tEXtSoftwarewww.inkscape.org<IDATXMHTQͤF$!AA$ePk}HQѮuL"[4>\蓈qEڴ6)iF{Z43ck;;>ZẀ <)J*UF'ca pHYs f-4tEXtSoftwarewww.inkscape.org<IDATxlg^Ӗ-,0sQƀ%.)-6:,3&12e(ұЕeTf4Dm86?%]{?{ޯz}O<@DDDDDDDDDDDDDDDDDDDDDMZV,+Ϟ8z4[÷^Y&b4{02CR"iU-1u.17fM_!(B@qU}CDp̚7O%Ie92Њ0dp'D[:Bpqt[}?ivoFѯRv@X蟪DjL&͑MIZ-zf̑Mw>+'#=':w⣍׌T_`dh.Ըuk{"2>؜\9mhp+3Mu/W-Ysbc|ig'0@k3WVxWփ{T駚w>Zq?Y ໾Yk~V.x&نoCpK[YD_7%e\+gK{NE_ VXZk;K{zL{~N}\> G;3UWMvvkn08$Lvh"{%]^o#w?{sN Dܓ_P\VivSe Mzf&'+ݖO޾ڻ$wmǶM\]@[jkVxSݴh{ZU{HU @ 4a նDB5T5qKí]T6opu X#@.&N dxq{Q /^: /wJ;o/&j:wnl\"+Zu?PH~ ČDs`8pPyډ=~w֘b$W\Z*4ޗ 7O38E,Ea߱6~uKK["4eb"3UÚsݩ jp3>OuAo2S{l-kC1wMr@wxǏՋ@24pN>[ä5=ȥ(]:91u.UL rzP9:6ubs""""""""""""""""""""""""""*ETdIENDB`fortran-toml-0.5.0/assets/toml-f.svg0000664000175000017500000000612315201541453017547 0ustar alastairalastair fortran-toml-0.5.0/assets/toml-f.png0000664000175000017500000001376515201541453017546 0ustar alastairalastairPNG  IHDRM` pHYs.#.#x?vtEXtSoftwarewww.inkscape.org<IDATx{u}9 IXhhj VnYoemvzZ;;S&25 "Lmږ". B {;Ϸ$}}9La&{~I>{~?k_jbfv^twkwntHIt @F(U2BJPdR #*T @F(U2BJPd%:4J[Ζ|ϖ4WْuN_ҐI]zV2="LKMiWI:OҬ܇{e3(UMwaT II7&i֫oYx~\.'%]J/4w~s5=YOPJıUr;-:\vǸE._?O%_g4>8 iT:*ӆAtCpWݒvݲr]rvd"i\3|k'T4FH:6\,B=T\N޷mG.0٪RZvDRyt]=kGkXQȾ[d:uk:I-h pt2efwg?ڈةhxox{zItI{1z@ J@sR!j֕ߏN/V騴}k`-J@CNI/ % ΀P ݼ)P{*N(8+M>9T4#$o?xd a8:>T4JG$Wcyf#J@Cڿ?{ӵ];9T4$3 nT4$3+8بێ  .=I҂wX/8rFh8bfҗ#G J@qRsJ qK$YRKV, r9:!nQK^ Δ^챗T1(I:7+9jeU'PtCNtTJ{wù<PyIBC ]a(U %1]3  ͋PuT K56mS Xl۝z62PƦE_- &'r}ĢT4 !Fh iT%nT qXob ChǿU{<6"Q3%G͎RߠRjr*O2(U&)6;J@c?pU_Ѱ@b|(UuQ)~<6qI9vOwG0\ᬼ֛C]-4s${:O;AR[F5UI{s\9IT\fY}\oik[_uΏ ;CGQg.\3MwEbTԍjR [?G &cH G(rHJ@qלI 1*k)*^ R!'{ ΀:@($xHT *EfpK)U І;WiT ܂їJ)U ,~HkuRPh.ydJ@a^Lo48*š$M S1n*jxP ,Rڳl` #*KCw.dH ǃRPHJdFf0UJ@! =3OR[h8>*Br=D_dOC(!gw/x 3* ߩ,ipJ@.*!!%?J@$3%Ff0}P Td%JǍRP8mm3΀:D( uRP(.Ef07L Pe )a(U!%/J@)|P(U')ٮKvb3^Q q Đ&RPCC.PK )a(UX5|HɒRńQ ÕD0 c*!{{f@=TBcFf0 )aR(U0t$M>B(/uo )a(U`7ӌ<5 s*BpEwmf@Tt;EҙJ@!+>FpH)CJ EO]@Zhj BWw=rp h(Tؼtf0-J@ +7>T0CJh8*ƧN'8 JN=`(U]{ScSCJ w-4z*9')!{*ܙşT=J@1d )!s*\}k~ RK֜%e)!% W-D?xT=wF(UT hL*ܸL:?2I߻j h\*lbْN\T䦔`H)eH C CJht*8T(JI? hp4+7?&g%IlZjukƟ=)##Ͻ^ !!% +ɓ/Jzy 'H;VX_}@ ht7ILW>g{D댋yR!REQ@rFI巪vҾ;:TӴǿ9J]sWJ5Id15a_ZXn $=g@Tko_?S-IIsΕlZdJ1oRLz~n|׽ Ԙ?`Vt:ly!%$%RjhI:;:\[wi:_RjEg8-KUtT$y{n.@}TzKt/~1EAҔ=إ"7*P[Dfx*NRB~(UF>n}Ytx^89T1+͎" p[URjr4+ƭwaTI<5x3P@؁ Z\tϓz2Gf@sTK\tqߗG> _*P#S̞p(rci{cH 9TٶmcRvw4|wT/JsIEx[~YV/Z=]QY6rwp4J!3GthWZkzb%:u8v2Q@ 'Fg8h{~bE8D!%DTRj]sHu&N۷/ЇſOu{CJjI=f`{זד$Ug1(U 'uY8et+|#/>$ CJA9ܶ?&m}+֬Gͳ$ymФBO +~#RqJ 2BJPdR #*T @F(U2BJPdR #*T @F(U2BJPdR #*?eUIENDB`fortran-toml-0.5.0/assets/toml-f-favicon.svg0000664000175000017500000000612215201541453021171 0ustar alastairalastair fortran-toml-0.5.0/assets/toml-f-64x64.png0000664000175000017500000000233615201541453020327 0ustar alastairalastairPNG  IHDR@@iq pHYs^QEltEXtSoftwarewww.inkscape.org<kIDATx_h[Uǿf]eMc{RUT|R&KJ7MeSY֤IJN2aL?t⟊j&|ps$I@;sh4Fh4k ?sbgSSїSc{ii7 r*A$c6^јtg{l0ss `Lu_wqV5QI52}T.)S'"_; ip .iQ<7=(ՕhBQ;T\sGy[9"8ؤx6+5SJW,1bCW@ [ޢX3 |H~cZޭ&MGw2>ż G97$CɖDx#1ph`@ʛ4I]hbt9?W  @r/`AvaZG6J`b_ub_ー<ɱGkd-l֫23-ls/%+dEuK"j%@/k:L UESu%m @hcƃE.ZLO sS@?hLVdza⹮!WNp VL7Np&Va(5q\+@zIAwy\+Ix @l]] Pb̅7+ ijWJfsO s]N])T/ aO`֋u˭ٹSK3 3 ~M~'Ɨ5{v[nCOC- a5f!9><<\00b*@6%6@u/Yt>M0XȪ?sͩ`t}% Os0j>=q+#I Mw3w u>f:3ss-&?@{mP/BXQecLOFO8x%a 2"ҍP#Y9WUv^m<,3i)1Kxh4Fh4;lnPIENDB`fortran-toml-0.5.0/test/0000775000175000017500000000000015201541453015303 5ustar alastairalastairfortran-toml-0.5.0/test/unit/0000775000175000017500000000000015201541453016262 5ustar alastairalastairfortran-toml-0.5.0/test/unit/build.f900000664000175000017500000010272315201541453017706 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Testsuite for the `tomlf_build` modules module tftest_build use testdrive use tomlf_build use tomlf implicit none private public :: collect_build contains !> Collect all exported unit tests subroutine collect_build(testsuite) !> Collection of tests type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & & new_unittest("array-real-sp", array_real_sp), & & new_unittest("array-real-dp", array_real_dp), & & new_unittest("array-int-i1", array_int_i1), & & new_unittest("array-int-i2", array_int_i2), & & new_unittest("array-int-i4", array_int_i4), & & new_unittest("array-int-i8", array_int_i8), & & new_unittest("array-bool", array_bool), & & new_unittest("array-datetime", array_datetime), & & new_unittest("array-string", array_string), & & new_unittest("array-merge", array_merge), & & new_unittest("table-table", table_table), & & new_unittest("table-array", table_array), & & new_unittest("table-real-sp", table_real_sp), & & new_unittest("table-real-dp", table_real_dp), & & new_unittest("table-int-i1", table_int_i1), & & new_unittest("table-int-i2", table_int_i2), & & new_unittest("table-int-i4", table_int_i4), & & new_unittest("table-int-i8", table_int_i8), & & new_unittest("table-bool", table_bool), & & new_unittest("table-dateime", table_datetime), & & new_unittest("table-string", table_string), & & new_unittest("table-merge-append", table_merge_append), & & new_unittest("table-merge-overwrite", table_merge_overwrite)] end subroutine collect_build !> Check double precision floating point numbers (default) subroutine table_real_dp(error) use tomlf_constants, only : tf_dp, tfi use tomlf_type, only : new_table, toml_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table real(tf_dp), parameter :: in1 = 1.0_tf_dp, in2 = huge(in1), in3 = tiny(in1) real(tf_dp) :: val integer :: stat call new_table(table) call set_value(table, "real", in2, stat=stat) call get_value(table, "real", val, stat=stat) call check(error, val, in2, rel=.true.) if (allocated(error)) return call table%delete("real") call set_value(table, toml_key("real"), in1, stat=stat) call get_value(table, toml_key("real"), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%delete("real") call set_value(table, toml_path("real", "sub", "sub", "sub"), in1, stat=stat) call get_value(table, toml_path("real", "sub", "sub", "sub"), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, "real", val, in3, stat=stat) call check(error, val, in3) if (allocated(error)) return call set_value(table, "int", 1_tfi, stat=stat) call get_value(table, "int", val, stat=stat) call check(error, val, 1.0_tf_dp) if (allocated(error)) return call set_value(table, "str", "1", stat=stat) call get_value(table, "str", val, stat=stat) call check(error, stat, toml_stat%type_mismatch) if (allocated(error)) return end subroutine table_real_dp !> Check single precision floating point numbers !> !> Since TOML requires to store them as double precision numbers, we might find !> larger deviations here than just epsilon. subroutine table_real_sp(error) use tomlf_constants, only : tf_sp, tfi use tomlf_type, only : new_table, toml_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table real(tf_sp), parameter :: in1 = 1.0_tf_sp, in2 = huge(in1), in3 = tiny(in1) real(tf_sp), parameter :: thr = 2*epsilon(0.0_tf_sp) real(tf_sp) :: val integer :: stat call new_table(table) call set_value(table, toml_key("real"), in2, stat=stat) call get_value(table, toml_key("real"), val, stat=stat) call check(error, val, in2, thr=thr, rel=.true.) if (allocated(error)) return call table%delete("real") call set_value(table, "real", in1, stat=stat) call get_value(table, "real", val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%delete("real") call set_value(table, toml_path("real", "sub"), in1, stat=stat) call get_value(table, toml_path("real", "sub"), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, "real", val, in3, stat=stat) call check(error, val, in3) if (allocated(error)) return call set_value(table, "int", 1_tfi, stat=stat) call get_value(table, "int", val, stat=stat) call check(error, val, 1.0_tf_sp) if (allocated(error)) return call set_value(table, "str", "1", stat=stat) call get_value(table, "str", val, stat=stat) call check(error, stat, toml_stat%type_mismatch) if (allocated(error)) return end subroutine table_real_sp !> Check smallest integers (char) subroutine table_int_i1(error) use tomlf_constants, only : tf_i1, tfi use tomlf_type, only : new_table, toml_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table integer(tf_i1), parameter :: in1 = 1_tf_i1, in2 = huge(in1), in3 = -huge(in1) integer(tf_i1) :: val integer :: stat ! table uninitialized call set_value(table, toml_key("int"), in2, stat=stat) call get_value(table, "int", val, stat=stat) call check(error, val, in2) if (allocated(error)) return call table%delete("int") call set_value(table, "int", in1, stat=stat) call get_value(table, toml_key("int"), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%delete("int") call set_value(table, toml_path("int", "sub"), in1, stat=stat) call get_value(table, toml_path("int", "sub"), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, "int", val, in3, stat=stat) call check(error, val, in3) if (allocated(error)) return call set_value(table, "huge", huge(1_tfi), stat=stat) call get_value(table, "huge", val, stat=stat) call check(error, stat, toml_stat%conversion_error) if (allocated(error)) return call set_value(table, "str", "1", stat=stat) call get_value(table, "str", val, stat=stat) call check(error, stat, toml_stat%type_mismatch) if (allocated(error)) return call get_value(table, "missing", val, stat=stat) call check(error, stat, toml_stat%missing_key) if (allocated(error)) return end subroutine table_int_i1 !> Check short integers subroutine table_int_i2(error) use tomlf_constants, only : tf_i2, tfi use tomlf_type, only : new_table, toml_table !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table integer(tf_i2), parameter :: in1 = 1_tf_i2, in2 = huge(in1), in3 = -huge(in1) integer(tf_i2) :: val integer :: stat call new_table(table) call set_value(table, toml_key("int"), in2, stat=stat) call get_value(table, toml_key("int"), val, stat=stat) call check(error, val, in2) if (allocated(error)) return call table%delete("int") call set_value(table, "int", in1, stat=stat) call get_value(table, "int", val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%delete("int") call set_value(table, toml_path("int", "sub"), in1, stat=stat) call get_value(table, toml_path("int", "sub"), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, "int", val, in3, stat=stat) call check(error, val, in3) if (allocated(error)) return call set_value(table, "huge", huge(1_tfi), stat=stat) call get_value(table, "huge", val, stat=stat) call check(error, stat, toml_stat%conversion_error) if (allocated(error)) return call set_value(table, "str", "1", stat=stat) call get_value(table, "str", val, stat=stat) call check(error, stat, toml_stat%type_mismatch) if (allocated(error)) return call get_value(table, "missing", val, stat=stat) call check(error, stat, toml_stat%missing_key) if (allocated(error)) return end subroutine table_int_i2 !> Check default integers subroutine table_int_i4(error) use tomlf_constants, only : tf_i4, tfi use tomlf_type, only : new_table, toml_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table integer(tf_i4), parameter :: in1 = 1_tf_i4, in2 = huge(in1), in3 = -huge(in1) integer(tf_i4) :: val integer :: stat call new_table(table) call set_value(table, "int", in2, stat=stat) call get_value(table, "int", val, stat=stat) call check(error, val, in2) if (allocated(error)) return call table%delete("int") call set_value(table, toml_key("int"), in1, stat=stat) call get_value(table, toml_key("int"), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%delete("int") call set_value(table, toml_path("int", "sub", "sub"), in1, stat=stat) call get_value(table, toml_path("int", "sub", "sub"), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, "int", val, in3, stat=stat) call check(error, val, in3) if (allocated(error)) return call set_value(table, "huge", huge(1_tfi), stat=stat) call get_value(table, "huge", val, stat=stat) call check(error, stat, toml_stat%conversion_error) if (allocated(error)) return call set_value(table, "str", "1", stat=stat) call get_value(table, "str", val, stat=stat) call check(error, stat, toml_stat%type_mismatch) if (allocated(error)) return call get_value(table, "missing", val, stat=stat) call check(error, stat, toml_stat%missing_key) if (allocated(error)) return end subroutine table_int_i4 !> Check long integers (default) subroutine table_int_i8(error) use tomlf_constants, only : tf_i8 use tomlf_type, only : new_table, toml_table, toml_key use tomlf_utils, only : to_string !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table integer :: ii integer(tf_i8), parameter :: in1 = 1_tf_i8, in2 = huge(in1), in3 = -huge(in1) integer(tf_i8) :: val integer :: stat call new_table(table) call set_value(table, "int", in2, stat=stat) call get_value(table, "int", val, stat=stat) call check(error, val, in2) if (allocated(error)) return call table%delete("int") call set_value(table, toml_key("int"), in1, stat=stat) call get_value(table, "int", val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%delete("int") call set_value(table, toml_path([toml_key("int"), toml_key("sub")]), in1, stat=stat) call get_value(table, toml_path([toml_key("int"), toml_key("sub")]), val, stat=stat) call check(error, val, in1) if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, toml_key("int"), val, in3, stat=stat) call check(error, val, in3) if (allocated(error)) return call set_value(table, "str", "1", stat=stat) call get_value(table, "str", val, stat=stat) call check(error, stat, toml_stat%type_mismatch) if (allocated(error)) return call table%destroy() call new_table(table) do ii = 1, 100 call set_value(table, to_string(ii), int(ii, tf_i8), stat=stat) end do call get_value(table, to_string(100), val, stat=stat) call check(error, val, 100_tf_i8) if (allocated(error)) return call get_value(table, "missing", val, stat=stat) call check(error, stat, toml_stat%missing_key) if (allocated(error)) return end subroutine table_int_i8 !> Check logicals subroutine table_bool(error) use tomlf_type, only : toml_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table logical, parameter :: true = .true., false = .false. logical :: val integer :: stat table = toml_table() call set_value(table, toml_key("logic"), true, stat=stat) call get_value(table, toml_key("logic"), val, stat=stat) call check(error, val, true) if (allocated(error)) return call table%delete("logic") call set_value(table, "logic", false, stat=stat) call get_value(table, "logic", val, stat=stat) call check(error, val, false) if (allocated(error)) return call table%delete("logic") call set_value(table, toml_path("logic", "sub"), false, stat=stat) call get_value(table, toml_path("logic", "sub"), val, stat=stat) call check(error, val, false) if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, "logic", val, true, stat=stat) call check(error, val, true) if (allocated(error)) return call set_value(table, "str", "1", stat=stat) call get_value(table, "str", val, stat=stat) call check(error, stat, toml_stat%type_mismatch) if (allocated(error)) return call get_value(table, "missing", val, stat=stat) call check(error, stat, toml_stat%missing_key) if (allocated(error)) return end subroutine table_bool !> Check datetime subroutine table_datetime(error) use tomlf_type, only : toml_table, toml_key use tomlf_datetime, only : toml_datetime, toml_date, toml_time, operator(==), to_string !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table type(toml_datetime) :: val, ts1, ts2 integer :: stat ts1 = toml_datetime(toml_date(2022, 7, 31), toml_time(13, 51, 42)) ts2 = toml_datetime(toml_date(2019, 12, 17), toml_time(18, 26, 59)) table = toml_table() call set_value(table, toml_key("datetime"), ts1, stat=stat) call get_value(table, toml_key("datetime"), val, stat=stat) call check(error, val == ts1, & & "Expected '"//to_string(ts1)//"' but got '"//to_string(val)//"'") if (allocated(error)) return call table%delete("datetime") call set_value(table, "datetime", ts2, stat=stat) call get_value(table, "datetime", val, stat=stat) call check(error, val == ts2, & & "Expected '"//to_string(ts2)//"' but got '"//to_string(val)//"'") if (allocated(error)) return call table%delete("datetime") call set_value(table, toml_path("datetime", "sub", "sub"), ts2, stat=stat) call get_value(table, toml_path("datetime", "sub", "sub"), val, stat=stat) call check(error, val == ts2, & & "Expected '"//to_string(ts2)//"' but got '"//to_string(val)//"'") if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, "datetime", val, ts1, stat=stat) call check(error, val == ts1, & & "Expected '"//to_string(ts1)//"' but got '"//to_string(val)//"'") if (allocated(error)) return call set_value(table, "str", "1", stat=stat) call get_value(table, "str", val, stat=stat) call check(error, stat, toml_stat%type_mismatch) if (allocated(error)) return call get_value(table, "missing", val, stat=stat) call check(error, stat, toml_stat%missing_key) if (allocated(error)) return end subroutine table_datetime !> Check strings subroutine table_string(error) use tomlf_type, only : toml_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table character(len=:), allocatable :: val integer :: stat table = toml_table() call set_value(table, toml_key("string"), "value", stat=stat) call get_value(table, "string", val, stat=stat) call check(error, val, "value") if (allocated(error)) return call table%delete("string") call set_value(table, "string", """value""", stat=stat) call get_value(table, "string", val, stat=stat) call check(error, val, """value""") if (allocated(error)) return call table%delete("string") call set_value(table, toml_path("string", "sub"), "value", stat=stat) call get_value(table, toml_path("string", "sub"), val, stat=stat) call check(error, val, "value") if (allocated(error)) return call table%destroy call new_table(table) call get_value(table, toml_key("string"), val, "'value'", stat=stat) call check(error, val, "'value'") if (allocated(error)) return call get_value(table, "missing", val, stat=stat) call check(error, stat, toml_stat%missing_key) if (allocated(error)) return end subroutine table_string subroutine table_table(error) use tomlf_type, only : new_table, toml_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table type(toml_table), pointer :: child integer :: stat call new_table(table) call get_value(table, toml_key("table-of-tables"), child, & & requested=.false.) call check(error, .not.associated(child), "Should not create table") if (allocated(error)) return call get_value(table, toml_key("table-of-tables"), child, & & requested=.false., stat=stat) call check(error, .not.associated(child), "Should not create table") if (allocated(error)) return call check(error, stat, toml_stat%success, "Status should be success since key is not requested") if (allocated(error)) return end subroutine table_table subroutine table_array(error) use tomlf_type, only : toml_value, new_table, toml_table, add_table, new_array, & & toml_array, toml_key, add_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table type(toml_table), pointer :: child type(toml_array), pointer :: children class(toml_value), allocatable :: val integer :: i, stat call new_table(table) call get_value(table, toml_key("array-of-tables"), children, & & requested=.false., stat=stat) call check(error, stat, "Not finding a not required value shouldn't be an error") if (allocated(error)) return call add_array(table, "array-of-tables", children, stat) do i = 1, 4 call add_table(children, child, stat) call set_value(child, "val", i*i) end do call check(error, len(children), 4) if (allocated(error)) return call children%pop(val) call check(error, len(children), 3) if (allocated(error)) return select type(val) class default call test_failed(error, "Popped value is not of table type") class is (toml_table) call get_value(val, "val", i) call check(error, i, 16) end select if (allocated(error)) return call children%shift(val) call check(error, len(children), 2) if (allocated(error)) return select type(val) class default call test_failed(error, "Shifted value is not of table type") class is (toml_table) call get_value(val, "val", i) call check(error, i, 1) end select if (allocated(error)) return call get_value(table, toml_path("tables", "with", "array"), children, & & requested=.false., stat=stat) call check(error, .not.associated(children), "Should not create array") if (allocated(error)) return call check(error, stat, "Not finding a not required value shouldn't be an error") if (allocated(error)) return call get_value(table, toml_path("tables", "with", "array"), children, & & requested=.true.) call check(error, associated(children), "Array was not created") if (allocated(error)) return end subroutine table_array !> Check single precision floating point numbers subroutine array_real_sp(error) use tomlf_constants, only : tf_sp use tomlf_type, only : new_array, toml_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array real(tf_sp) :: val real(tf_sp), allocatable :: vals(:) integer :: ii integer :: stat ! array not initialized do ii = 1, 10 call set_value(array, ii, sqrt(real(ii, tf_sp)), stat=stat) end do call check(error, len(array), 10) if (allocated(error)) return ii = 7 call get_value(array, ii, val, stat=stat) call check(error, val, sqrt(7.0_tf_sp), rel=.true.) if (allocated(error)) return call get_value(array, vals, stat=stat) call check(error, allocated(vals)) call check(error, vals(7), val, rel=.true.) if (allocated(error)) return vals = 2*vals(:9) call set_value(array, vals, stat=stat) call check(error, len(array), 9) if (allocated(error)) return call get_value(array, ii, val, stat=stat) call check(error, val, 2*sqrt(7.0_tf_sp), rel=.true.) if (allocated(error)) return end subroutine array_real_sp !> Check double precision floating point numbers (default) subroutine array_real_dp(error) use tomlf_constants, only : tf_dp use tomlf_type, only : new_array, toml_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array real(tf_dp) :: val real(tf_dp), allocatable :: vals(:) integer :: ii integer :: stat call new_array(array) do ii = 1, 10 call set_value(array, ii, sqrt(real(ii, tf_dp)), stat=stat) end do call check(error, len(array), 10) if (allocated(error)) return ii = 7 call get_value(array, ii, val, stat=stat) call check(error, val, sqrt(7.0_tf_dp), rel=.true.) if (allocated(error)) return call get_value(array, vals, stat=stat) call check(error, allocated(vals)) call check(error, vals(7), val, rel=.true.) if (allocated(error)) return vals = 2*vals(:9) call set_value(array, vals, stat=stat) call check(error, len(array), 9) if (allocated(error)) return call get_value(array, ii, val, stat=stat) call check(error, val, 2*sqrt(7.0_tf_dp), rel=.true.) if (allocated(error)) return end subroutine array_real_dp !> Check smallest integers (char) subroutine array_int_i1(error) use tomlf_constants, only : tf_i1 use tomlf_type, only : new_array, toml_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array integer(tf_i1) :: val integer(tf_i1), allocatable :: vals(:) integer(tf_i1), parameter :: ref(4) = [integer(tf_i1) :: 1, 3, 5, 7] integer :: ii integer :: stat call new_array(array) do ii = 1, size(ref) call set_value(array, ii, ref(ii), stat=stat) end do call check(error, len(array), size(ref)) if (allocated(error)) return ii = 3 call get_value(array, ii, val, stat=stat) call check(error, val, ref(ii)) if (allocated(error)) return call get_value(array, vals, stat=stat) call check(error, allocated(vals)) call check(error, vals(ii), val) if (allocated(error)) return vals = -vals(:ii) call set_value(array, vals, stat=stat) call check(error, len(array), ii) if (allocated(error)) return call get_value(array, ii, val, stat=stat) call check(error, val, -ref(ii)) if (allocated(error)) return end subroutine array_int_i1 !> Check short integers subroutine array_int_i2(error) use tomlf_constants, only : tf_i2 use tomlf_type, only : new_array, toml_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array integer(tf_i2) :: val integer(tf_i2), allocatable :: vals(:) integer(tf_i2), parameter :: ref(4) = [integer(tf_i2) :: 1, 3, 5, 7] integer :: ii integer :: stat call new_array(array) do ii = 1, size(ref) call set_value(array, ii, ref(ii), stat=stat) end do call check(error, len(array), size(ref)) if (allocated(error)) return ii = 3 call get_value(array, ii, val, stat=stat) call check(error, val, ref(ii)) if (allocated(error)) return call get_value(array, vals, stat=stat) call check(error, allocated(vals)) call check(error, vals(ii), val) if (allocated(error)) return vals = -vals(:ii) call set_value(array, vals, stat=stat) call check(error, len(array), ii) if (allocated(error)) return call get_value(array, ii, val, stat=stat) call check(error, val, -ref(ii)) if (allocated(error)) return end subroutine array_int_i2 !> Check default integers subroutine array_int_i4(error) use tomlf_constants, only : tf_i4 use tomlf_type, only : new_array, toml_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array integer(tf_i4) :: val integer(tf_i4), allocatable :: vals(:) integer(tf_i4), parameter :: ref(4) = [integer(tf_i4) :: 1, 3, 5, 7] integer :: ii integer :: stat call new_array(array) do ii = 1, size(ref) call set_value(array, ii, ref(ii), stat=stat) end do call check(error, len(array), size(ref)) if (allocated(error)) return ii = 3 call get_value(array, ii, val, stat=stat) call check(error, val, ref(ii)) if (allocated(error)) return call get_value(array, vals, stat=stat) call check(error, allocated(vals)) call check(error, vals(ii), val) if (allocated(error)) return vals = -vals(:ii) call set_value(array, vals, stat=stat) call check(error, len(array), ii) if (allocated(error)) return call get_value(array, ii, val, stat=stat) call check(error, val, -ref(ii)) if (allocated(error)) return end subroutine array_int_i4 !> Check long integers (default) subroutine array_int_i8(error) use tomlf_constants, only : tf_i8 use tomlf_type, only : new_array, toml_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array integer(tf_i8) :: val integer(tf_i8), allocatable :: vals(:) integer(tf_i8), parameter :: ref(4) = [integer(tf_i8) :: 1, 3, 5, 7] integer :: ii integer :: stat call new_array(array) do ii = 1, size(ref) call set_value(array, ii, ref(ii), stat=stat) end do call check(error, len(array), size(ref)) if (allocated(error)) return ii = 3 call get_value(array, ii, val, stat=stat) call check(error, val, ref(ii)) if (allocated(error)) return call get_value(array, vals, stat=stat) call check(error, allocated(vals)) call check(error, vals(ii), val) if (allocated(error)) return vals = -vals(:ii) call set_value(array, vals, stat=stat) call check(error, len(array), ii) if (allocated(error)) return call get_value(array, ii, val, stat=stat) call check(error, val, -ref(ii)) if (allocated(error)) return call array%destroy() call new_array(array) do ii = 1, 100 call set_value(array, ii, int(ii, tf_i8), stat=stat) end do call check(error, len(array), 100) if (allocated(error)) return end subroutine array_int_i8 !> Check logicals subroutine array_bool(error) use tomlf_type, only : toml_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array logical :: val logical, allocatable :: vals(:) integer :: ii integer :: stat array = toml_array() do ii = 1, 10 call set_value(array, ii, mod(ii, 2) == 1, stat=stat) end do call check(error, len(array), 10) if (allocated(error)) return ii = 3 call get_value(array, ii, val, stat=stat) call check(error, val, mod(ii, 2) == 1) if (allocated(error)) return call get_value(array, vals, stat=stat) call check(error, allocated(vals)) call check(error, vals(7), val) if (allocated(error)) return vals = .not.vals(:9) call set_value(array, vals, stat=stat) call check(error, len(array), 9) if (allocated(error)) return call get_value(array, ii, val, stat=stat) call check(error, val, mod(ii, 2) /= 1) if (allocated(error)) return end subroutine array_bool !> Check datetime subroutine array_datetime(error) use tomlf_type, only : toml_array, len use tomlf_datetime, only : toml_datetime, toml_date, toml_time, operator(==), to_string !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array type(toml_datetime) :: val, ts type(toml_datetime), allocatable :: vals(:) integer :: ii integer :: stat array = toml_array() do ii = 1, 10 call set_value(array, ii, toml_datetime(toml_date(2022, ii, 7), toml_time()), stat=stat) end do call check(error, len(array), 10) if (allocated(error)) return ii = 3 call get_value(array, ii, val, stat=stat) ts = toml_datetime(toml_date(2022, ii, 7), toml_time()) call check(error, val == ts, & & "Expected '"//to_string(ts)//"' but got '"//to_string(val)//"'") if (allocated(error)) return call get_value(array, vals, stat=stat) call check(error, vals(ii) == val, & & "Expected '"//to_string(val)//"' but got '"//to_string(vals(ii))//"'") if (allocated(error)) return vals = [(toml_datetime(toml_date(2022, ii, 8), toml_time()), ii = 1, 9)] call set_value(array, vals, stat=stat) call check(error, len(array), 9) if (allocated(error)) return call get_value(array, ii, val, stat=stat) call check(error, val == vals(ii), & & "Expected '"//to_string(vals(ii))//"' but got '"//to_string(val)//"'") if (allocated(error)) return end subroutine array_datetime !> Check datetime subroutine array_string(error) use tomlf_type, only : toml_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array character(:), allocatable :: val integer :: ii integer :: stat array = toml_array() do ii = 1, 10 call set_value(array, ii, repeat("a", ii), stat=stat) end do call check(error, len(array), 10) if (allocated(error)) return ii = 3 call get_value(array, ii, val, stat=stat) call check(error, val, repeat("a", ii)) if (allocated(error)) return end subroutine array_string !> Merge two arrays subroutine array_merge(error) use tomlf_type, only : toml_array, new_array, len !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_array) :: array1, array2 integer :: val1, val2 integer :: ii integer :: stat array1 = toml_array() do ii = 1, 4 call set_value(array1, ii, ii*ii, stat=stat) end do call new_array(array2) do ii = 1, 6 call set_value(array2, ii, ii*ii, stat=stat) end do call merge_array(array1, array2) call array2%destroy call check(error, len(array1), 10) if (allocated(error)) return call get_value(array1, 2, val1, stat) call get_value(array1, 6, val2, stat) call check(error, val1, val2) if (allocated(error)) return end subroutine array_merge !> Merge two tables subroutine table_merge_append(error) use tomlf_type, only : toml_table, add_table, new_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table1, table2 type(toml_table), pointer :: child type(toml_key), allocatable :: list(:) real :: val integer :: stat table1 = toml_table() call set_value(table1, "first", 10, stat) call set_value(table1, "second", "string", stat) call set_value(table1, "third", 1.0e-7, stat) call add_table(table1, "fourth", child, stat) call set_value(child, "child", .true., stat) call new_table(table2) call set_value(table2, "val", 12, stat) call set_value(table2, "key", "content", stat) call set_value(table2, "entry", -1.0e-7, stat) call set_value(table2, "third", .false., stat) call add_table(table2, "section", child, stat) call set_value(child, "sub", .false., stat) call add_table(table2, "fourth", child, stat) call merge_table(table1, table2) call table2%destroy call table1%get_keys(list) call check(error, size(list), 8) if (allocated(error)) return call get_value(table1, "third", val, stat=stat) call check(error, stat) if (allocated(error)) return end subroutine table_merge_append !> Merge two tables subroutine table_merge_overwrite(error) use tomlf_type, only : toml_table, add_table, new_table, toml_key !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table1, table2 type(toml_table), pointer :: child type(toml_key), allocatable :: list(:) logical :: val integer :: stat table1 = toml_table() call set_value(table1, "first", 10, stat) call set_value(table1, "second", "string", stat) call set_value(table1, "third", 1.0e-7, stat) call add_table(table1, "fourth", child, stat) call set_value(child, "child", .true., stat) call new_table(table2) call set_value(table2, "val", 12, stat) call set_value(table2, "key", "content", stat) call set_value(table2, "entry", -1.0e-7, stat) call set_value(table2, "third", .false., stat) call add_table(table2, "section", child, stat) call set_value(child, "sub", .false., stat) call add_table(table2, "fourth", child, stat) call merge_table(table1, table2, toml_merge_config(keyval="overwrite")) call table2%destroy call table1%get_keys(list) call check(error, size(list), 8) if (allocated(error)) return call get_value(table1, "third", val, stat=stat) call check(error, stat) if (allocated(error)) return end subroutine table_merge_overwrite end module tftest_build fortran-toml-0.5.0/test/unit/meson.build0000664000175000017500000000210215201541453020417 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. testdrive_dep = dependency( 'test-drive', fallback: ['test-drive', 'testdrive_dep'], default_options: ['default_library=static'], static: true, ) unittests = [ 'build', 'lexer', 'parser', 'ser', 'sort', 'utils', ] test_srcs = files('main.f90') foreach t : unittests test_srcs += files('@0@.f90'.format(t)) endforeach tester = executable( 'tftester', sources: test_srcs, dependencies: [tomlf_dep, testdrive_dep], ) foreach t : unittests test( 'tftest-@0@'.format(t), tester, args: [t], ) endforeach fortran-toml-0.5.0/test/unit/sort.f900000664000175000017500000000702015201541453017570 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tftest_sort use testdrive use tomlf, only : toml_key, sort implicit none private public :: collect_sort contains !> Collect all exported unit tests subroutine collect_sort(testsuite) !> Collection of tests type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & & new_unittest("sorted", test_sorted), & & new_unittest("reversed", test_reversed), & & new_unittest("unsorted", test_unsorted) & & ] end subroutine collect_sort subroutine test_sorted(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_key), allocatable :: list(:) integer :: i character(len=*), parameter :: expected(10) = & & ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] allocate(list(0)) ! avoid compiler warning list = [ & & toml_key("0"), & & toml_key("1"), & & toml_key("2"), & & toml_key("3"), & & toml_key("4"), & & toml_key("5"), & & toml_key("6"), & & toml_key("7"), & & toml_key("8"), & & toml_key("9")] call sort(list) do i = 1, size(list) if (list(i)%key /= expected(i)) then call test_failed(error, "List is not sorted correctly") exit end if end do if (allocated(error)) return end subroutine test_sorted subroutine test_reversed(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_key), allocatable :: list(:) integer :: i character(len=*), parameter :: expected(10) = & & ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] allocate(list(0)) ! avoid compiler warning list = [ & & toml_key("9"), & & toml_key("8"), & & toml_key("7"), & & toml_key("6"), & & toml_key("5"), & & toml_key("4"), & & toml_key("3"), & & toml_key("2"), & & toml_key("1"), & & toml_key("0")] call sort(list) do i = 1, size(list) if (list(i)%key /= expected(i)) then call test_failed(error, "List is not sorted correctly") exit end if end do if (allocated(error)) return end subroutine test_reversed subroutine test_unsorted(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_key), allocatable :: list(:) integer :: i character(len=*), parameter :: expected(10) = & & ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] allocate(list(0)) ! avoid compiler warning list = [ & & toml_key("7"), & & toml_key("9"), & & toml_key("6"), & & toml_key("8"), & & toml_key("5"), & & toml_key("2"), & & toml_key("4"), & & toml_key("1"), & & toml_key("3"), & & toml_key("0")] call sort(list) do i = 1, size(list) if (list(i)%key /= expected(i)) then call test_failed(error, "List is not sorted correctly") exit end if end do if (allocated(error)) return end subroutine test_unsorted end module tftest_sort fortran-toml-0.5.0/test/unit/utils.f900000664000175000017500000000421615201541453017745 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tftest_utils use testdrive implicit none private public :: collect_utils contains !> Collect all exported unit tests subroutine collect_utils(testsuite) !> Collection of tests type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & & new_unittest("string-i1", string_i1), & & new_unittest("string-i2", string_i2), & & new_unittest("string-i4", string_i4), & & new_unittest("string-i8", string_i8)] end subroutine collect_utils subroutine string_i1(error) use tomlf_constants, only : tf_i1 use tomlf_utils, only : to_string !> Error handling type(error_type), allocatable, intent(out) :: error call check(error, to_string(-huge(1_tf_i1) - 1_tf_i1), "-128") end subroutine string_i1 subroutine string_i2(error) use tomlf_constants, only : tf_i2 use tomlf_utils, only : to_string !> Error handling type(error_type), allocatable, intent(out) :: error call check(error, to_string(-huge(1_tf_i2) - 1_tf_i2), "-32768") end subroutine string_i2 subroutine string_i4(error) use tomlf_constants, only : tf_i4 use tomlf_utils, only : to_string !> Error handling type(error_type), allocatable, intent(out) :: error call check(error, to_string(-huge(1_tf_i4) - 1_tf_i4), "-2147483648") end subroutine string_i4 subroutine string_i8(error) use tomlf_constants, only : tfi use tomlf_utils, only : to_string !> Error handling type(error_type), allocatable, intent(out) :: error call check(error, to_string(-huge(1_tfi) - 1_tfi), "-9223372036854775808") end subroutine string_i8 end module tftest_utils fortran-toml-0.5.0/test/unit/main.f900000664000175000017500000000536715201541453017541 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Wrapper for the testsuites program tftester use, intrinsic :: iso_fortran_env, only : error_unit use testdrive, only : run_testsuite, new_testsuite, testsuite_type, & & select_suite, run_selected, get_argument use tftest_build, only : collect_build use tftest_lexer, only : collect_lexer use tftest_parser, only : collect_parser use tftest_ser, only : collect_ser use tftest_sort, only : collect_sort use tftest_utils, only : collect_utils implicit none integer :: stat, is character(len=:), allocatable :: suite_name, test_name type(testsuite_type), allocatable :: testsuites(:) character(len=*), parameter :: fmt = '("#", *(1x, a))' stat = 0 allocate(testsuites(0)) ! avoid compiler warning testsuites = [ & & new_testsuite("build", collect_build), & & new_testsuite("lexer", collect_lexer), & & new_testsuite("parser", collect_parser), & & new_testsuite("ser", collect_ser), & & new_testsuite("sort", collect_sort), & & new_testsuite("utils", collect_utils) & & ] call get_argument(1, suite_name) call get_argument(2, test_name) if (allocated(suite_name)) then is = select_suite(testsuites, suite_name) if (is > 0 .and. is <= size(testsuites)) then if (allocated(test_name)) then write(error_unit, fmt) "Suite:", testsuites(is)%name call run_selected(testsuites(is)%collect, test_name, error_unit, stat) if (stat < 0) then error stop 1 end if else write(error_unit, fmt) "Testing:", testsuites(is)%name call run_testsuite(testsuites(is)%collect, error_unit, stat) end if else write(error_unit, fmt) "Available testsuites" do is = 1, size(testsuites) write(error_unit, fmt) "-", testsuites(is)%name end do error stop 1 end if else do is = 1, size(testsuites) write(error_unit, fmt) "Testing:", testsuites(is)%name call run_testsuite(testsuites(is)%collect, error_unit, stat) end do end if if (stat > 0) then write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" error stop 1 end if end program tftester fortran-toml-0.5.0/test/unit/parser.f900000664000175000017500000007517515201541453020115 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tftest_parser use testdrive use tomlf_constants, only : nl => TOML_NEWLINE use tomlf_de_parser use tomlf_de_lexer, only : toml_lexer, new_lexer_from_string, toml_token, token_kind use tomlf_error, only : toml_error use tomlf_type, only : toml_table use tomlf_terminal, only : toml_terminal implicit none public :: collect_parser type, extends(toml_lexer) :: mocked_lexer type(toml_token), allocatable :: token(:) contains procedure :: next end type mocked_lexer contains !> Collect all exported unit tests subroutine collect_parser(testsuite) !> Collection of tests type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & & new_unittest("root-invalid", root_invalid, should_fail=.true.), & & new_unittest("table-body", table_body), & & new_unittest("table-header-long", table_header_long), & & new_unittest("table-header-empty", table_header_empty, should_fail=.true.), & & new_unittest("table-header-unclosed1", table_header_unclosed1, should_fail=.true.), & & new_unittest("table-header-unclosed2", table_header_unclosed2, should_fail=.true.), & & new_unittest("table-header-invalid", table_header_invalid, should_fail=.true.), & & new_unittest("table-header-newline1", table_header_newline1, should_fail=.true.), & & new_unittest("table-header-newline2", table_header_newline2, should_fail=.true.), & & new_unittest("table-header-duplicate", table_header_duplicate, should_fail=.true.), & & new_unittest("table-header-trailing-whitespace", table_header_trailing_whitespace), & & new_unittest("table-header-trailing-comment", table_header_trailing_comment), & & new_unittest("aot-body", aot_body), & & new_unittest("aot-inline", aot_inline, should_fail=.true.), & & new_unittest("aot-header-whitespace1", aot_header_whitespace1), & & new_unittest("aot-header-whitespace2", aot_header_whitespace2), & & new_unittest("aot-header-empty", aot_header_empty, should_fail=.true.), & & new_unittest("aot-header-unclosed1", aot_header_unclosed1, should_fail=.true.), & & new_unittest("aot-header-unclosed2", aot_header_unclosed2, should_fail=.true.), & & new_unittest("aot-header-unclosed3", aot_header_unclosed3, should_fail=.true.), & & new_unittest("aot-header-duplicate1", aot_header_duplicate1, should_fail=.true.), & & new_unittest("aot-header-duplicate2", aot_header_duplicate2, should_fail=.true.), & & new_unittest("aot-header-duplicate3", aot_header_duplicate3, should_fail=.true.), & & new_unittest("aot-header-invalid1", aot_header_invalid1, should_fail=.true.), & & new_unittest("aot-header-invalid2", aot_header_invalid2, should_fail=.true.), & & new_unittest("keyval-empty", keyval_empty, should_fail=.true.), & & new_unittest("keyval-equal", keyval_equal, should_fail=.true.), & & new_unittest("keyval-dotted", keyval_dotted), & & new_unittest("keyval-comment", keyval_comment), & & new_unittest("inline-array", inline_array), & & new_unittest("inline-array-empty", inline_array_empty), & & new_unittest("inline-array-whitespace", inline_array_whitespace), & & new_unittest("inline-array-newline", inline_array_newline), & & new_unittest("inline-array-nested", inline_array_nested), & & new_unittest("inline-table", inline_table), & & new_unittest("inline-table-empty", inline_table_empty), & & new_unittest("inline-table-whitespace", inline_table_whitespace), & & new_unittest("inline-table-newline", inline_table_newline), & & new_unittest("inline-table-comma", inline_table_comma), & & new_unittest("inline-table-modify", inline_table_modify, should_fail=.true.), & & new_unittest("empty", empty)] end subroutine collect_parser subroutine empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "", [toml_token(token_kind%eof)]) end subroutine empty subroutine root_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "}", & & [toml_token(token_kind%lbrace, 1, 1), toml_token(token_kind%eof, 2, 2)]) end subroutine root_invalid subroutine table_body(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a]"//nl//"c=1"//nl//"[b]"//nl//"c=1", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%rbracket, 3, 3), toml_token(token_kind%newline, 4, 4), & & toml_token(token_kind%keypath, 5, 5), toml_token(token_kind%equal, 6, 6), & & toml_token(token_kind%int, 7, 7), toml_token(token_kind%newline, 8, 8), & & toml_token(token_kind%lbracket, 9, 9), toml_token(token_kind%keypath, 10, 10), & & toml_token(token_kind%rbracket, 11, 11), toml_token(token_kind%newline, 12, 12), & & toml_token(token_kind%keypath, 13, 13), toml_token(token_kind%equal, 14, 14), & & toml_token(token_kind%int, 15, 15), toml_token(token_kind%eof, 16, 16)]) end subroutine table_body subroutine aot_body(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[[a]]"//nl//"b=1"//nl//"[[a]]"//nl//"b=1", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%lbracket, 2, 2), & & toml_token(token_kind%keypath, 3, 3), toml_token(token_kind%rbracket, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%newline, 6, 6), & & toml_token(token_kind%keypath, 7, 7), toml_token(token_kind%equal, 8, 8), & & toml_token(token_kind%int, 9, 9), toml_token(token_kind%newline, 10, 10), & & toml_token(token_kind%lbracket, 11, 11), toml_token(token_kind%lbracket, 12, 12), & & toml_token(token_kind%keypath, 13, 13), toml_token(token_kind%rbracket, 14, 14), & & toml_token(token_kind%rbracket, 15, 15), toml_token(token_kind%newline, 16, 16), & & toml_token(token_kind%keypath, 17, 17), toml_token(token_kind%equal, 18, 18), & & toml_token(token_kind%int, 19, 19), toml_token(token_kind%eof, 20, 20)]) end subroutine aot_body subroutine table_header_long(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%dot, 3, 3), toml_token(token_kind%keypath, 4, 4), & & toml_token(token_kind%dot, 5, 5), toml_token(token_kind%keypath, 6, 6), & & toml_token(token_kind%dot, 7, 7), toml_token(token_kind%keypath, 8, 8), & & toml_token(token_kind%dot, 9, 9), toml_token(token_kind%keypath, 10, 10), & & toml_token(token_kind%dot, 11, 11), toml_token(token_kind%keypath, 12, 12), & & toml_token(token_kind%dot, 13, 13), toml_token(token_kind%keypath, 14, 14), & & toml_token(token_kind%dot, 15, 15), toml_token(token_kind%keypath, 16, 16), & & toml_token(token_kind%dot, 17, 17), toml_token(token_kind%keypath, 18, 18), & & toml_token(token_kind%dot, 19, 19), toml_token(token_kind%keypath, 20, 20), & & toml_token(token_kind%dot, 21, 21), toml_token(token_kind%keypath, 22, 22), & & toml_token(token_kind%dot, 23, 23), toml_token(token_kind%keypath, 24, 24), & & toml_token(token_kind%dot, 25, 25), toml_token(token_kind%keypath, 26, 26), & & toml_token(token_kind%dot, 27, 27), toml_token(token_kind%keypath, 28, 28), & & toml_token(token_kind%dot, 29, 29), toml_token(token_kind%keypath, 30, 30), & & toml_token(token_kind%dot, 31, 31), toml_token(token_kind%keypath, 32, 32), & & toml_token(token_kind%dot, 33, 33), toml_token(token_kind%keypath, 34, 34), & & toml_token(token_kind%dot, 35, 35), toml_token(token_kind%keypath, 36, 36), & & toml_token(token_kind%dot, 37, 37), toml_token(token_kind%keypath, 38, 38), & & toml_token(token_kind%dot, 39, 39), toml_token(token_kind%keypath, 40, 40), & & toml_token(token_kind%dot, 41, 41), toml_token(token_kind%keypath, 42, 42), & & toml_token(token_kind%dot, 43, 43), toml_token(token_kind%keypath, 44, 44), & & toml_token(token_kind%dot, 45, 45), toml_token(token_kind%keypath, 46, 46), & & toml_token(token_kind%dot, 47, 47), toml_token(token_kind%keypath, 48, 48), & & toml_token(token_kind%dot, 49, 49), toml_token(token_kind%keypath, 50, 50), & & toml_token(token_kind%dot, 51, 51), toml_token(token_kind%keypath, 52, 52), & & toml_token(token_kind%rbracket, 53, 53), toml_token(token_kind%eof, 54, 54)]) end subroutine table_header_long subroutine table_header_empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[]", & & [toml_token(token_kind%lbracket, 60, 1), toml_token(token_kind%rbracket, 2, 2), & & toml_token(token_kind%eof, 15, 9)]) end subroutine table_header_empty subroutine table_header_unclosed1(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%eof, 2, 2)]) end subroutine table_header_unclosed1 subroutine table_header_unclosed2(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a.]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%dot, 3, 3), toml_token(token_kind%rbracket, 4, 4), & & toml_token(token_kind%eof, 5, 5)]) end subroutine table_header_unclosed2 subroutine table_header_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a,]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%comma, 3, 3), toml_token(token_kind%rbracket, 4, 4), & & toml_token(token_kind%eof, 5, 5)]) end subroutine table_header_invalid subroutine table_header_newline1(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[""\n""]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%string, 2, 5), & & toml_token(token_kind%rbracket, 6, 6), toml_token(token_kind%eof, 7, 7)]) end subroutine table_header_newline1 subroutine table_header_newline2(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a] [b]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%rbracket, 3, 3), toml_token(token_kind%whitespace, 4, 4), & & toml_token(token_kind%lbracket, 5, 5), toml_token(token_kind%keypath, 6, 6), & & toml_token(token_kind%rbracket, 7, 7), toml_token(token_kind%eof, 8, 8)]) end subroutine table_header_newline2 subroutine table_header_duplicate(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a]"//nl//"[a]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%rbracket, 3, 3), toml_token(token_kind%newline, 4, 4), & & toml_token(token_kind%lbracket, 5, 5), toml_token(token_kind%keypath, 6, 6), & & toml_token(token_kind%rbracket, 7, 7), toml_token(token_kind%eof, 5, 5)]) end subroutine table_header_duplicate subroutine table_header_trailing_whitespace(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a.b] ", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%dot, 3, 3), toml_token(token_kind%keypath, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%whitespace, 6, 6), & & toml_token(token_kind%eof, 7, 7)]) end subroutine table_header_trailing_whitespace subroutine table_header_trailing_comment(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a.b]#", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%dot, 3, 3), toml_token(token_kind%keypath, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%comment, 6, 6), & & toml_token(token_kind%eof, 7, 7)]) end subroutine table_header_trailing_comment subroutine aot_header_whitespace1(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[[ a]]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%lbracket, 2, 2), & & toml_token(token_kind%whitespace, 3, 3), toml_token(token_kind%keypath, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%rbracket, 6, 6), & & toml_token(token_kind%eof, 7, 7)]) end subroutine aot_header_whitespace1 subroutine aot_header_whitespace2(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[[a ]]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%lbracket, 2, 2), & & toml_token(token_kind%keypath, 3, 3), toml_token(token_kind%whitespace, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%rbracket, 6, 6), & & toml_token(token_kind%eof, 7, 7)]) end subroutine aot_header_whitespace2 subroutine aot_header_empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[[]]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%lbracket, 2, 2), & & toml_token(token_kind%rbracket, 3, 3), toml_token(token_kind%rbracket, 4, 4), & & toml_token(token_kind%eof, 5, 5)]) end subroutine aot_header_empty subroutine aot_header_unclosed1(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[[", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%lbracket, 2, 2), & & toml_token(token_kind%eof, 3, 3)]) end subroutine aot_header_unclosed1 subroutine aot_header_unclosed2(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[[a.]]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%lbracket, 2, 2), & & toml_token(token_kind%keypath, 3, 3), toml_token(token_kind%dot, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%rbracket, 6, 6), & & toml_token(token_kind%eof, 7, 7)]) end subroutine aot_header_unclosed2 subroutine aot_header_unclosed3(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[ [a] #", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%whitespace, 2, 2), & & toml_token(token_kind%lbracket, 3, 3), toml_token(token_kind%keypath, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%whitespace, 6, 7), & & toml_token(token_kind%comment, 8, 8), toml_token(token_kind%eof, 9, 9)]) end subroutine aot_header_unclosed3 subroutine aot_header_duplicate1(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[[a]]"//nl//"[a]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%lbracket, 2, 2), & & toml_token(token_kind%keypath, 3, 3), toml_token(token_kind%rbracket, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%newline, 6, 6), & & toml_token(token_kind%lbracket, 7, 7), toml_token(token_kind%keypath, 8, 8), & & toml_token(token_kind%rbracket, 9, 9), toml_token(token_kind%eof, 10, 10)]) end subroutine aot_header_duplicate1 subroutine aot_header_duplicate2(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a]"//nl//"[[a]]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%rbracket, 3, 3), toml_token(token_kind%newline, 4, 4), & & toml_token(token_kind%lbracket, 5, 5), toml_token(token_kind%lbracket, 6, 6), & & toml_token(token_kind%keypath, 7, 7), toml_token(token_kind%rbracket, 8, 8), & & toml_token(token_kind%rbracket, 9, 9), toml_token(token_kind%eof, 10, 10)]) end subroutine aot_header_duplicate2 subroutine aot_header_duplicate3(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[a.b.c]"//nl//"[[a.b]]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%keypath, 2, 2), & & toml_token(token_kind%dot, 3, 3), toml_token(token_kind%keypath, 4, 4), & & toml_token(token_kind%dot, 5, 5), toml_token(token_kind%keypath, 6, 6), & & toml_token(token_kind%rbracket, 7, 7), toml_token(token_kind%newline, 8, 8), & & toml_token(token_kind%lbracket, 9, 9), toml_token(token_kind%lbracket, 10, 10), & & toml_token(token_kind%keypath, 11, 11), toml_token(token_kind%dot, 12, 12), & & toml_token(token_kind%keypath, 13, 13), toml_token(token_kind%rbracket, 14, 14), & & toml_token(token_kind%rbracket, 15, 15), toml_token(token_kind%eof, 16, 16)]) end subroutine aot_header_duplicate3 subroutine aot_header_invalid1(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[ [a]]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%whitespace, 2, 2), & & toml_token(token_kind%lbracket, 3, 3), toml_token(token_kind%keypath, 4, 4), & & toml_token(token_kind%rbracket, 5, 5), toml_token(token_kind%rbracket, 6, 6), & & toml_token(token_kind%eof, 6, 6)]) end subroutine aot_header_invalid1 subroutine aot_header_invalid2(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "[[a] ]", & & [toml_token(token_kind%lbracket, 1, 1), toml_token(token_kind%lbracket, 2, 2), & & toml_token(token_kind%keypath, 3, 3), toml_token(token_kind%rbracket, 4, 4), & & toml_token(token_kind%whitespace, 5, 5), toml_token(token_kind%rbracket, 6, 6), & & toml_token(token_kind%eof, 6, 6)]) end subroutine aot_header_invalid2 subroutine aot_inline(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a=[]"//nl//"[[a]]", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbracket, 3, 3), toml_token(token_kind%rbracket, 4, 4), & & toml_token(token_kind%newline, 6, 6), toml_token(token_kind%lbracket, 6, 6), & & toml_token(token_kind%lbracket, 7, 7), toml_token(token_kind%keypath, 8, 8), & & toml_token(token_kind%rbracket, 9, 9), toml_token(token_kind%rbracket, 10, 10), & & toml_token(token_kind%eof, 6, 6)]) end subroutine aot_inline subroutine keyval_empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a=", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%eof, 3, 3)]) end subroutine keyval_empty subroutine keyval_equal(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a=,", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%comma, 3, 3), toml_token(token_kind%eof, 4, 4)]) end subroutine keyval_equal subroutine keyval_dotted(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a.b.c=1", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%dot, 2, 2), & & toml_token(token_kind%keypath, 3, 3), toml_token(token_kind%dot, 4, 4), & & toml_token(token_kind%keypath, 5, 5), toml_token(token_kind%equal, 6, 6), & & toml_token(token_kind%int, 7, 7), toml_token(token_kind%eof, 8, 8)]) end subroutine keyval_dotted subroutine keyval_comment(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "# This is a full-line comment"//nl//& & "key = ""value"" # This is a comment at the end of a line", & & [toml_token(token_kind%comment, 1, 29), toml_token(token_kind%newline, 30, 30), & & toml_token(token_kind%keypath, 31, 33), toml_token(token_kind%whitespace, 34, 34), & & toml_token(token_kind%equal, 35, 35), toml_token(token_kind%whitespace, 36, 36), & & toml_token(token_kind%string, 37, 43), toml_token(token_kind%whitespace, 44, 44), & & toml_token(token_kind%comment, 45, 85), toml_token(token_kind%eof, 86, 86)]) end subroutine keyval_comment subroutine inline_array(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a=[1,[2,],[3]]", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbracket, 3, 3), toml_token(token_kind%int, 4, 4), & & toml_token(token_kind%comma, 5, 5), toml_token(token_kind%lbracket, 6, 6), & & toml_token(token_kind%int, 7, 7), toml_token(token_kind%comma, 8, 8), & & toml_token(token_kind%rbracket, 9, 9), toml_token(token_kind%comma, 10, 10), & & toml_token(token_kind%lbracket, 11, 11), toml_token(token_kind%int, 12, 12), & & toml_token(token_kind%rbracket, 13, 13), toml_token(token_kind%rbracket, 14, 14), & & toml_token(token_kind%eof, 15, 15)]) end subroutine inline_array subroutine inline_array_empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a=[]", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbracket, 3, 3), toml_token(token_kind%rbracket, 4, 4), & & toml_token(token_kind%eof, 5, 5)]) end subroutine inline_array_empty subroutine inline_array_whitespace(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a=[ 1 , ]", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbracket, 3, 3), toml_token(token_kind%whitespace, 4, 4), & & toml_token(token_kind%int, 5, 5), toml_token(token_kind%whitespace, 6, 6), & & toml_token(token_kind%comma, 7, 7), toml_token(token_kind%whitespace, 8, 8), & & toml_token(token_kind%rbracket, 9, 9), toml_token(token_kind%eof, 10, 10)]) end subroutine inline_array_whitespace subroutine inline_array_newline(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a=[ #"//nl//"1 #"//nl//", #"//nl//"] #", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbracket, 3, 3), toml_token(token_kind%whitespace, 4, 4), & & toml_token(token_kind%comment, 5, 5), toml_token(token_kind%newline, 6, 6), & & toml_token(token_kind%int, 7, 7), toml_token(token_kind%whitespace, 8, 8), & & toml_token(token_kind%comment, 9, 9), toml_token(token_kind%newline, 10, 10), & & toml_token(token_kind%comma, 11, 11), toml_token(token_kind%whitespace, 12, 12), & & toml_token(token_kind%comment, 13, 13), toml_token(token_kind%newline, 14, 14), & & toml_token(token_kind%rbracket, 15, 15), toml_token(token_kind%whitespace, 16, 16), & & toml_token(token_kind%comment, 17, 17), toml_token(token_kind%eof, 18, 18)]) end subroutine inline_array_newline subroutine inline_array_nested(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "data=[[""gamma"",""delta""],[1,2]]", & & [toml_token(token_kind%keypath, 1, 4), toml_token(token_kind%equal, 5, 5), & & toml_token(token_kind%lbracket, 6, 6), toml_token(token_kind%lbracket, 7, 7), & & toml_token(token_kind%string, 8, 14), toml_token(token_kind%comma, 15, 15), & & toml_token(token_kind%string, 16, 22), toml_token(token_kind%rbracket, 23, 23), & & toml_token(token_kind%comma, 24, 24), toml_token(token_kind%lbracket, 25, 25), & & toml_token(token_kind%int, 26, 26), toml_token(token_kind%comma, 27, 27), & & toml_token(token_kind%int, 28, 28), toml_token(token_kind%rbracket, 29, 29), & & toml_token(token_kind%rbracket, 30, 30), toml_token(token_kind%eof, 31, 31)]) end subroutine inline_array_nested subroutine inline_table(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a={b=1,c.d=2}", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbrace, 3, 3), toml_token(token_kind%keypath, 4, 4), & & toml_token(token_kind%equal, 5, 5), toml_token(token_kind%int, 6, 6), & & toml_token(token_kind%comma, 7, 7), toml_token(token_kind%keypath, 8, 8), & & toml_token(token_kind%dot, 9, 9), toml_token(token_kind%keypath, 10, 10), & & toml_token(token_kind%equal, 11, 11), toml_token(token_kind%int, 12, 12), & & toml_token(token_kind%rbrace, 13, 13), toml_token(token_kind%eof, 14, 14)]) end subroutine inline_table subroutine inline_table_empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a={}", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbrace, 3, 3), toml_token(token_kind%rbrace, 4, 4), & & toml_token(token_kind%eof, 5, 5)]) end subroutine inline_table_empty subroutine inline_table_empty_whitespace(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a={ }", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbrace, 3, 3), toml_token(token_kind%whitespace, 4, 4), & & toml_token(token_kind%rbrace, 5, 5), toml_token(token_kind%eof, 6, 6)]) end subroutine inline_table_empty_whitespace subroutine inline_table_whitespace(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a={ b=1 }", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbrace, 3, 3), toml_token(token_kind%whitespace, 4, 4), & & toml_token(token_kind%keypath, 5, 5), toml_token(token_kind%equal, 6, 6), & & toml_token(token_kind%int, 7, 7), toml_token(token_kind%whitespace, 8, 8), & & toml_token(token_kind%rbrace, 9, 9), toml_token(token_kind%eof, 10, 10)]) end subroutine inline_table_whitespace subroutine inline_table_newline(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a={ b=1"//nl//"}", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbrace, 3, 3), toml_token(token_kind%whitespace, 4, 4), & & toml_token(token_kind%keypath, 5, 5), toml_token(token_kind%equal, 6, 6), & & toml_token(token_kind%int, 7, 7), toml_token(token_kind%newline, 8, 8), & & toml_token(token_kind%rbrace, 9, 9), toml_token(token_kind%eof, 10, 10)]) end subroutine inline_table_newline subroutine inline_table_comma(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a={ b=1,}", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbrace, 3, 3), toml_token(token_kind%whitespace, 4, 4), & & toml_token(token_kind%keypath, 5, 5), toml_token(token_kind%equal, 6, 6), & & toml_token(token_kind%int, 7, 7), toml_token(token_kind%comma, 8, 8), & & toml_token(token_kind%rbrace, 9, 9), toml_token(token_kind%eof, 10, 10)]) end subroutine inline_table_comma subroutine inline_table_modify(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_parser(error, "a={}"//nl//"[a.b]", & & [toml_token(token_kind%keypath, 1, 1), toml_token(token_kind%equal, 2, 2), & & toml_token(token_kind%lbrace, 3, 3), toml_token(token_kind%rbrace, 4, 4), & & toml_token(token_kind%newline, 5, 5), toml_token(token_kind%lbracket, 6, 6), & & toml_token(token_kind%keypath, 7, 7), toml_token(token_kind%dot, 8, 8), & & toml_token(token_kind%keypath, 9, 9), toml_token(token_kind%rbracket, 10, 10), & & toml_token(token_kind%eof, 11, 11)]) end subroutine inline_table_modify subroutine check_parser(error, string, token) !> Error handling type(error_type), allocatable, intent(out) :: error character(len=*), intent(in) :: string type(toml_token), intent(in) :: token(:) type(mocked_lexer) :: lexer type(toml_error), allocatable :: parse_error type(toml_table), allocatable :: table call new_lexer(lexer, string, token) call parse(lexer, table, config=toml_parser_config(color=.true.), error=parse_error) call move_error(error, parse_error) if (allocated(error)) return call check(error, allocated(table)) end subroutine check_parser !> Create a new lexer subroutine new_lexer(lexer, string, token) type(mocked_lexer), intent(out) :: lexer character(len=*), intent(in) :: string type(toml_token), intent(in) :: token(:) lexer%filename = "mocked" lexer%pos = 0 lexer%chunk = string lexer%token = token end subroutine new_lexer subroutine next(lexer, token) class(mocked_lexer), intent(inout) :: lexer type(toml_token), intent(inout) :: token lexer%pos = lexer%pos + 1 token = lexer%token(lexer%pos) end subroutine next subroutine move_error(error, parse_error) type(error_type), allocatable, intent(out) :: error type(toml_error), intent(in), optional :: parse_error if (present(parse_error)) then allocate(error) error%stat = 1 error%message = parse_error%message end if end subroutine move_error end module tftest_parser fortran-toml-0.5.0/test/unit/lexer.f900000664000175000017500000022655515201541453017740 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tftest_lexer use testdrive use tomlf_constants, only : tfi, tfr, nl => TOML_NEWLINE use tomlf_datetime, only : toml_datetime, toml_date, toml_time, operator(==), to_string use tomlf_de_lexer use tomlf_error, only : toml_error implicit none public :: collect_lexer contains !> Collect all exported unit tests subroutine collect_lexer(testsuite) !> Collection of tests type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & & new_unittest("bool-true", bool_true), & & new_unittest("bool-true-or-not", bool_nottrue), & & new_unittest("bool-false", bool_false), & & new_unittest("bool-falsey", bool_falsey), & & new_unittest("brace", brace), & & new_unittest("brace-left", brace_left), & & new_unittest("brace-right", brace_right), & & new_unittest("brace-unclosed", brace_unclosed), & & new_unittest("bracket", bracket), & & new_unittest("bracket-left", bracket_left), & & new_unittest("bracket-right", bracket_right), & & new_unittest("bracket-unclosed", bracket_unclosed), & & new_unittest("comma", comma), & & new_unittest("comment", comment), & & new_unittest("comment-eof", comment_eof), & & new_unittest("datetime", datetime), & & new_unittest("datetime-comment", datetime_comment), & & new_unittest("datetime-local-date", datetime_local_date), & & new_unittest("datetime-local-time", datetime_local_time), & & new_unittest("datetime-local", datetime_local), & & new_unittest("datetime-milliseconds", datetime_milliseconds), & & new_unittest("datetime-timezone", datetime_timezone), & & new_unittest("datetime-hour-over", datetime_hour_over), & & new_unittest("datetime-mday-over", datetime_mday_over), & & new_unittest("datetime-mday-under", datetime_mday_under), & & new_unittest("datetime-mday-leap", datetime_mday_leap), & & new_unittest("datetime-minute-over", datetime_minute_over), & & new_unittest("datetime-month-over", datetime_month_over), & & new_unittest("datetime-month-under", datetime_month_under), & & new_unittest("datetime-no-leading-zeros", datetime_no_leading_zeros), & & new_unittest("datetime-no-seconds", datetime_no_seconds), & & new_unittest("datetime-no-seconds-local", datetime_no_seconds_local), & & new_unittest("datetime-no-seconds-offset", datetime_no_seconds_offset), & & new_unittest("datetime-no-seconds-localtime", datetime_no_seconds_localtime), & & new_unittest("datetime-no-separator", datetime_no_separator), & & new_unittest("datetime-trailing-separator", datetime_trailing_separator), & & new_unittest("datetime-local-timezone", datetime_local_timezone), & & new_unittest("datetime-second-over", datetime_second_over), & & new_unittest("datetime-leap-year-invalid", datetime_leap_year_invalid), & & new_unittest("datetime-leap-year-century", datetime_leap_year_century), & & new_unittest("datetime-month-days-30", datetime_month_days_30), & & new_unittest("datetime-month-days-31", datetime_month_days_31), & & new_unittest("datetime-feb-days-invalid", datetime_feb_days_invalid), & & new_unittest("datetime-month-days-30-more", datetime_month_days_30_more), & & new_unittest("datetime-month-days-31-more", datetime_month_days_31_more), & & new_unittest("datetime-time-boundaries", datetime_time_boundaries), & & new_unittest("datetime-tz-zero-offset", datetime_tz_zero_offset), & & new_unittest("datetime-milliseconds-precision", datetime_milliseconds_precision), & & new_unittest("datetime-year-boundaries", datetime_year_boundaries), & & new_unittest("dot-keypath", dot_keypath), & & new_unittest("dot-invalid", dot_invalid), & & new_unittest("empty", empty), & & new_unittest("equal", equal), & & new_unittest("float-nan", float_nan), & & new_unittest("float-inf", float_inf), & & new_unittest("float-point", float_point), & & new_unittest("float-exponent", float_exponent), & & new_unittest("float-underscore", float_underscore), & & new_unittest("float-zero", float_zero), & & new_unittest("float-double-point", float_double_point), & & new_unittest("float-double-expo", float_double_expo), & & new_unittest("float-dunderscored", float_dunderscored), & & new_unittest("float-invalid", float_invalid), & & new_unittest("float-leading-underscore", float_leading_underscore), & & new_unittest("float-trailing-underscore", float_trailing_underscore), & & new_unittest("integer-zero", integer_zero), & & new_unittest("integer-limits", integer_limits), & & new_unittest("integer-hexadecimal", integer_hexadecimal), & & new_unittest("integer-octal", integer_octal), & & new_unittest("integer-binary", integer_binary), & & new_unittest("integer-leading-zero", integer_leading_zero), & & new_unittest("integer-underscored", integer_underscored), & & new_unittest("integer-dunderscored", integer_dunderscored), & & new_unittest("integer-trailing_underscore", integer_trailing_underscore), & & new_unittest("keypath", keypath), & & new_unittest("keypath-dotted", keypath_dotted), & & new_unittest("keypath-whitespace", keypath_whitespace), & & new_unittest("keypath-string", keypath_string), & & new_unittest("keypath-inline-table", keypath_inline_table), & & new_unittest("keypath-inline-array", keypath_inline_array), & & new_unittest("keypath-inline-array-nested", keypath_inline_array_nested), & & new_unittest("keypath-inline-unclosed", keypath_inline_unclosed), & & new_unittest("keypath-invalid", keypath_invalid), & & new_unittest("keypath-escape", keypath_escape), & & new_unittest("newline-lf", newline_lf), & & new_unittest("newline-crlf", newline_crlf), & & new_unittest("newline-cr", newline_cr), & & new_unittest("literal", literal), & & new_unittest("literal-unclosed", literal_unclosed), & & new_unittest("literal-control", literal_control), & & new_unittest("literal-triple", literal_triple), & & new_unittest("literal-multiline", literal_multiline), & & new_unittest("literal-multiline-five", literal_multiline_five), & & new_unittest("literal-multiline-six", literal_multiline_six), & & new_unittest("literal-multiline-unclosed", literal_multiline_unclosed), & & new_unittest("string", string), & & new_unittest("string-unclosed", string_unclosed), & & new_unittest("string-control", string_control), & & new_unittest("string-escape", string_escape), & & new_unittest("string-escape-invalid", string_escape_invalid), & & new_unittest("string-unicode-escape", string_unicode_escape), & & new_unittest("string-triple", string_triple), & & new_unittest("string-multiline", string_multiline), & & new_unittest("string-multiline-unclosed1", string_multiline_unclosed1), & & new_unittest("string-multiline-unclosed2", string_multiline_unclosed2), & & new_unittest("string-multiline-unclosed3", string_multiline_unclosed3), & & new_unittest("string-multiline-escape", string_multiline_escape), & & new_unittest("token-keypath-string", token_keypath_string), & & new_unittest("token-keypath-mstring", token_keypath_mstring), & & new_unittest("token-keypath-literal", token_keypath_literal), & & new_unittest("token-keypath-mliteral", token_keypath_mliteral), & & new_unittest("token-integer", token_integer), & & new_unittest("token-integer-binary", token_integer_binary), & & new_unittest("token-integer-octal", token_integer_octal), & & new_unittest("token-integer-hexadecimal", token_integer_hexadecimal), & & new_unittest("token-integer-twos-complement", token_integer_twos_complement), & & new_unittest("token-float", token_float), & & new_unittest("token-float-exceptional", token_float_exceptional), & & new_unittest("token-float-fuzz", token_float_fuzz), & & new_unittest("token-datetime", token_datetime), & & new_unittest("token-string", token_string), & & new_unittest("token-bool", token_bool), & & new_unittest("whitespace-blank", whitespace_blank), & & new_unittest("whitespace-tab", whitespace_tab), & & new_unittest("whitespace-mixed", whitespace_mixed), & & new_unittest("lexer-from-sequential", lexer_from_sequential), & & new_unittest("datetime-tz-max-positive", datetime_tz_max_positive), & & new_unittest("datetime-tz-max-negative", datetime_tz_max_negative), & & new_unittest("datetime-tz-hour-24", datetime_tz_hour_24), & & new_unittest("datetime-tz-minute-60", datetime_tz_minute_60), & & new_unittest("datetime-milliseconds-7digits", datetime_milliseconds_7digits), & & new_unittest("datetime-milliseconds-zeros", datetime_milliseconds_zeros), & & new_unittest("datetime-milliseconds-1digit", datetime_milliseconds_1digit), & & new_unittest("float-exponent-incomplete-plus", float_exponent_incomplete_plus), & & new_unittest("float-exponent-incomplete-minus", float_exponent_incomplete_minus), & & new_unittest("float-zero-exponent", float_zero_exponent), & & new_unittest("string-unicode-escape-2digits", string_unicode_escape_2digits), & & new_unittest("string-unicode-escape-3digits", string_unicode_escape_3digits), & & new_unittest("string-unicode-escape-5digits", string_unicode_escape_5digits), & & new_unittest("string-unicode-escape-cap-4digits", string_unicode_escape_cap_4digits), & & new_unittest("string-unicode-escape-cap-7digits", string_unicode_escape_cap_7digits), & & new_unittest("integer-hex-boundary", integer_hex_boundary), & & new_unittest("integer-hex-overflow", integer_hex_overflow), & & new_unittest("integer-octal-boundary", integer_octal_boundary), & & new_unittest("integer-binary-max-digits", integer_binary_max_digits), & & new_unittest("integer-underscore-after-prefix", integer_underscore_after_prefix), & & new_unittest("integer-underscore-hex-valid", integer_underscore_hex_valid), & & new_unittest("string-control-chars-escaped", string_control_chars_escaped), & & new_unittest("string-invalid-escape-v", string_invalid_escape_v), & & new_unittest("integer-hex-negative", integer_hex_negative), & & new_unittest("integer-hex-negative-boundary", integer_hex_negative_boundary), & & new_unittest("integer-hex-negative-overflow", integer_hex_negative_overflow), & & new_unittest("integer-octal-negative", integer_octal_negative), & & new_unittest("integer-octal-negative-boundary", integer_octal_negative_boundary), & & new_unittest("integer-octal-negative-overflow", integer_octal_negative_overflow), & & new_unittest("integer-binary-negative", integer_binary_negative), & & new_unittest("integer-binary-negative-boundary", integer_binary_negative_boundary), & & new_unittest("integer-binary-negative-overflow", integer_binary_negative_overflow)] end subroutine collect_lexer subroutine comment(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "# comment", & & [token_kind%comment, token_kind%eof], .false.) end subroutine comment subroutine comment_eof(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "# This is a full-line comment"//nl//& & "key = ""value"" # This is a comment at the end of a line", & & [token_kind%comment, token_kind%newline, token_kind%keypath, token_kind%whitespace, & & token_kind%equal, token_kind%whitespace, token_kind%string, token_kind%whitespace, & & token_kind%comment, token_kind%eof], .true.) end subroutine comment_eof subroutine empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "", & & [token_kind%eof], .false.) end subroutine empty subroutine brace(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "{}", & & [token_kind%lbrace, token_kind%rbrace, token_kind%eof], .false.) end subroutine brace subroutine brace_left(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "{", & & [token_kind%unclosed, token_kind%eof], .false.) end subroutine brace_left subroutine brace_unclosed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "a={{{", & & [token_kind%keypath, token_kind%equal, token_kind%unclosed, token_kind%unclosed, & & token_kind%unclosed, token_kind%eof], .true.) end subroutine brace_unclosed subroutine brace_right(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "}", & & [token_kind%rbrace, token_kind%eof], .false.) end subroutine brace_right subroutine bracket(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "[]", & & [token_kind%lbracket, token_kind%rbracket, token_kind%eof], .false.) end subroutine bracket subroutine bracket_left(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "[", & & [token_kind%lbracket, token_kind%eof], .true.) end subroutine bracket_left subroutine bracket_right(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "]", & & [token_kind%rbracket, token_kind%eof], .false.) end subroutine bracket_right subroutine bracket_unclosed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "a=[", & & [token_kind%keypath, token_kind%equal, token_kind%unclosed, token_kind%eof], .true.) end subroutine bracket_unclosed subroutine dot_keypath(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, ".", & & [token_kind%dot, token_kind%eof], .true.) end subroutine dot_keypath subroutine dot_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, ".", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine dot_invalid subroutine comma(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, ",", & & [token_kind%comma, token_kind%eof], .false.) end subroutine comma subroutine equal(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "=", & & [token_kind%equal, token_kind%eof], .false.) end subroutine equal subroutine keypath(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "abcd", & & [token_kind%keypath, token_kind%eof], .true.) end subroutine keypath subroutine keypath_dotted(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "3.14", & & [token_kind%keypath, token_kind%dot, token_kind%keypath, token_kind%eof], .true.) end subroutine keypath_dotted subroutine keypath_whitespace(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "a . b . c", & & [token_kind%keypath, token_kind%whitespace, token_kind%dot, token_kind%whitespace, & & token_kind%keypath, token_kind%whitespace, token_kind%dot, token_kind%whitespace, & & token_kind%keypath, token_kind%eof], .true.) end subroutine keypath_whitespace subroutine keypath_string(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "a.'b'.""c""", & & [token_kind%keypath, token_kind%dot, token_kind%literal, token_kind%dot, & & token_kind%string, token_kind%eof], .true.) end subroutine keypath_string subroutine keypath_inline_table(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "a.b={c.d=1,1.2e3=""abc"",""abc""=1.2e3}", & & [token_kind%keypath, token_kind%dot, token_kind%keypath, token_kind%equal, & & token_kind%lbrace, token_kind%keypath, token_kind%dot, token_kind%keypath, & & token_kind%equal, token_kind%int, token_kind%comma, token_kind%keypath, & & token_kind%dot, token_kind%keypath, token_kind%equal, token_kind%string, & & token_kind%comma, token_kind%string, token_kind%equal, token_kind%float, & & token_kind%rbrace, token_kind%eof], .true.) end subroutine keypath_inline_table subroutine keypath_inline_array(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "a.c=[""abc"",1.2e3,1,{d.f=2}]", & & [token_kind%keypath, token_kind%dot, token_kind%keypath, token_kind%equal, & & token_kind%lbracket, token_kind%string, token_kind%comma, token_kind%float, & & token_kind%comma, token_kind%int, token_kind%comma, token_kind%lbrace, & & token_kind%keypath, token_kind%dot, token_kind%keypath, token_kind%equal, & & token_kind%int, token_kind%rbrace, token_kind%rbracket, token_kind%eof], .true.) end subroutine keypath_inline_array subroutine keypath_inline_array_nested(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "data=[[""gamma"",""delta""],[1,2]]", & & [token_kind%keypath, token_kind%equal, token_kind%lbracket, token_kind%lbracket, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%rbracket, & & token_kind%comma, token_kind%lbracket, token_kind%int, token_kind%comma, & & token_kind%int, token_kind%rbracket, token_kind%rbracket, token_kind%eof], .true.) end subroutine keypath_inline_array_nested subroutine keypath_inline_unclosed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "a.c=[""abc"",1.2e3,1,{d.f=2]", & & [token_kind%keypath, token_kind%dot, token_kind%keypath, token_kind%equal, & & token_kind%unclosed, token_kind%string, token_kind%comma, token_kind%float, & & token_kind%comma, token_kind%int, token_kind%comma, token_kind%unclosed, & & token_kind%keypath, token_kind%dot, token_kind%keypath, token_kind%equal, & & token_kind%int, token_kind%rbracket, token_kind%eof], .true.) end subroutine keypath_inline_unclosed subroutine keypath_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "a:b", & & [token_kind%invalid, token_kind%eof], .true.) end subroutine keypath_invalid subroutine keypath_escape(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "\u00c0", & & [token_kind%invalid, token_kind%eof], .true.) end subroutine keypath_escape subroutine literal(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "'something\','anything'", & & [token_kind%literal, token_kind%comma, token_kind%literal, token_kind%eof], .false.) end subroutine literal subroutine literal_unclosed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "'right open"//new_line('a')//"'fully closed'", & & [token_kind%invalid, token_kind%newline, token_kind%literal, token_kind%eof], .false.) end subroutine literal_unclosed subroutine literal_control(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "'control char "//achar(0)//"','normal literal'", & & [token_kind%invalid, token_kind%comma, token_kind%literal, token_kind%eof], .false.) end subroutine literal_control subroutine literal_triple(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "'''something''','''anything'''", & & [token_kind%mliteral, token_kind%comma, token_kind%mliteral, token_kind%eof], .false.) end subroutine literal_triple subroutine literal_multiline(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "'''something"//new_line('a')//"anything'''", & & [token_kind%mliteral, token_kind%eof], .false.) end subroutine literal_multiline subroutine literal_multiline_five(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "'''something,anything'''''", & & [token_kind%mliteral, token_kind%eof], .false.) end subroutine literal_multiline_five subroutine literal_multiline_six(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "'''something,anything''''''", & & [token_kind%mliteral, token_kind%invalid, token_kind%eof], .false.) end subroutine literal_multiline_six subroutine literal_multiline_unclosed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "'''something,anything''"//new_line('a')//"a.key = 'value'", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine literal_multiline_unclosed subroutine string(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """something"",""anything""", & & [token_kind%string, token_kind%comma, token_kind%string, token_kind%eof], .false.) end subroutine string subroutine string_unclosed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """right open"//new_line('a')//"""fully closed""", & & [token_kind%invalid, token_kind%newline, token_kind%string, token_kind%eof], .false.) end subroutine string_unclosed subroutine string_control(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """control char "//achar(0)//""",""normal string""", & & [token_kind%invalid, token_kind%comma, token_kind%string, token_kind%eof], .false.) end subroutine string_control subroutine string_escape(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """a \b backspace character"",""a \t tab character"""//nl//& & """a \n new line character"",""a \f form feed character"""//nl//& & """a \r carriage return character"",""a \"" quote character"""//nl//& & """a \\ backslash character"",""a \e escape character"""//nl//& & """not a unicode \\u escape"",""not a unicode \u005Cu escape"""//nl//& & """not a unicode \\u0075 escape"",""not a unicode \\\u0075 escape"""//nl//& & """a \u007F delete control code"",""a \u001F unit separator control code""", & & [token_kind%string, token_kind%comma, token_kind%string, token_kind%newline, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%newline, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%newline, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%newline, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%newline, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%newline, & & token_kind%string, token_kind%comma, token_kind%string, token_kind%eof], .false.) end subroutine string_escape subroutine string_escape_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error character(len=*), parameter :: nl = new_line('a') call check_token(error, """""""something\ """""",""""""anything\u007""""""", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%eof], .false.) end subroutine string_escape_invalid subroutine string_unicode_escape(error) !> Error handling type(error_type), allocatable, intent(out) :: error character(len=*), parameter :: nl = new_line('a') call check_token(error, """\uD800"",""\ufffe""", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%eof], .false.) end subroutine string_unicode_escape subroutine string_triple(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """""""something"""""",""""""anything""""""", & & [token_kind%mstring, token_kind%comma, token_kind%mstring, token_kind%eof], .false.) end subroutine string_triple subroutine string_multiline(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """""""something"//new_line('a')//"anything""""""", & & [token_kind%mstring, token_kind%eof], .false.) end subroutine string_multiline subroutine string_multiline_unclosed1(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """""""", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine string_multiline_unclosed1 subroutine string_multiline_unclosed2(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """""""\", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine string_multiline_unclosed2 subroutine string_multiline_unclosed3(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """""""\""""""", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine string_multiline_unclosed3 subroutine string_multiline_escape(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, """""""\"//achar(10)//""""""","//& & """""""\"//achar(13)//achar(10)//""""""","//& & """""""value\n""""""", & & [token_kind%mstring, token_kind%comma, token_kind%mstring, token_kind%comma, & & token_kind%mstring, token_kind%eof], .false.) end subroutine string_multiline_escape subroutine whitespace_blank(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, " ", & & [token_kind%whitespace, token_kind%eof], .false.) end subroutine whitespace_blank subroutine whitespace_tab(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, achar(9)//achar(9), & & [token_kind%whitespace, token_kind%eof], .false.) end subroutine whitespace_tab subroutine whitespace_mixed(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, achar(9)//" "//achar(9), & & [token_kind%whitespace, token_kind%eof], .false.) end subroutine whitespace_mixed subroutine newline_lf(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, achar(13)//achar(10), & & [token_kind%newline, token_kind%eof], .false.) end subroutine newline_lf subroutine newline_crlf(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, achar(10), & & [token_kind%newline, token_kind%eof], .false.) end subroutine newline_crlf subroutine newline_cr(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, achar(13), & & [token_kind%invalid, token_kind%eof], .false.) end subroutine newline_cr subroutine bool_false(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "false", & & [token_kind%bool, token_kind%eof], .false.) end subroutine bool_false subroutine bool_falsey(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "falsey", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine bool_falsey subroutine bool_true(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "true", & & [token_kind%bool, token_kind%eof], .false.) end subroutine bool_true subroutine bool_nottrue(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "true-or-not", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine bool_nottrue subroutine float_nan(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "nan,-nan,+nan", & & [token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%eof], .false.) end subroutine float_nan subroutine float_inf(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "inf,-inf,+inf", & & [token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%eof], .false.) end subroutine float_inf subroutine float_point(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "3.14,+3.14,-3.14,0.123", & & [token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%eof], .false.) end subroutine float_point subroutine float_exponent(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "3e2,3E2,3e-2,3E+2,3e0,3.1e2,3.1E2,-1E-1", & & [token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%eof], .false.) end subroutine float_exponent subroutine float_underscore(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "3_141.5927,3141.592_7,3e1_4", & & [token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%eof], .false.) end subroutine float_underscore subroutine float_zero(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "0.0,+0.0,-0.0,0e0,0e00,+0e0,-0e0", & & [token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, token_kind%float, token_kind%comma, & & token_kind%float, token_kind%eof], .false.) end subroutine float_zero subroutine float_double_point(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "0..1,0.1.2", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%eof], .false.) end subroutine float_double_point subroutine float_double_expo(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1ee2,1e2e3", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%eof], .false.) end subroutine float_double_expo subroutine float_dunderscored(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1e2__3", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine float_dunderscored subroutine float_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1.2_,1.,+1.,-1.,03.14,+03.14,-03.14", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine float_invalid subroutine float_leading_underscore(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "_1e2,1e_2,1._2", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine float_leading_underscore subroutine float_trailing_underscore(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1e2_,1_e2,1_.2", & & [token_kind%invalid, token_kind%comma, token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine float_trailing_underscore subroutine integer_limits(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "9223372036854775807,-9223372036854775808", & & [token_kind%int, token_kind%comma, token_kind%int, token_kind%eof], .false.) end subroutine integer_limits subroutine integer_zero(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "0,+0,-0,0x0,0x00,0x00000,0o0,0o00,0o00000,0b0,0b00,0b00000", & & [token_kind%int, token_kind%comma, token_kind%int, token_kind%comma, & & token_kind%int, token_kind%comma, token_kind%int, token_kind%comma, & & token_kind%int, token_kind%comma, token_kind%int, token_kind%comma, & & token_kind%int, token_kind%comma, token_kind%int, token_kind%comma, & & token_kind%int, token_kind%comma, token_kind%int, token_kind%comma, & & token_kind%int, token_kind%comma, token_kind%int, token_kind%eof], .false.) end subroutine integer_zero subroutine integer_hexadecimal(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "0xFa1afe1,0x00987,0xcafebabe", & & [token_kind%int, token_kind%comma, token_kind%int, token_kind%comma, token_kind%int, & & token_kind%eof], .false.) end subroutine integer_hexadecimal subroutine integer_octal(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "0o766,0o01234567,0o7_6_5", & & [token_kind%int, token_kind%comma, token_kind%int, token_kind%comma, token_kind%int, & & token_kind%eof], .false.) end subroutine integer_octal subroutine integer_binary(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "0b1010,0b1_0_1,0b11010110", & & [token_kind%int, token_kind%comma, token_kind%int, token_kind%comma, token_kind%int, & & token_kind%eof], .false.) end subroutine integer_binary subroutine integer_leading_zero(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "001", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_leading_zero subroutine integer_underscored(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1_000,1_1_1_1", & & [token_kind%int, token_kind%comma, token_kind%int, token_kind%eof], .false.) end subroutine integer_underscored subroutine integer_dunderscored(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1__000", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_dunderscored subroutine integer_trailing_underscore(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1000_", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_trailing_underscore subroutine datetime(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1987-07-05 17:45:00Z,1987-07-05t17:45:00z", & & [token_kind%datetime, token_kind%comma, token_kind%datetime, token_kind%eof], .false.) end subroutine datetime subroutine datetime_comment(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1979-05-27 # Comment", & & [token_kind%datetime, token_kind%whitespace, token_kind%comment, token_kind%eof], & & .false.) end subroutine datetime_comment subroutine datetime_local_date(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1987-07-05", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_local_date subroutine datetime_local_time(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "17:45:00,10:32:00.555", & & [token_kind%datetime, token_kind%comma, token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_local_time subroutine datetime_local(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1987-07-05T17:45:00,1977-12-21T10:32:00.555,1987-07-05 17:45:00", & & [token_kind%datetime, token_kind%comma, token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_local subroutine datetime_milliseconds(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1987-07-05T17:45:56.1234Z,1987-07-05T17:45:56.6Z"//new_line('a')//& & "1987-07-05T17:45:56.1234+08:00,1987-07-05T17:45:56.6+08:00", & & [token_kind%datetime, token_kind%comma, token_kind%datetime, token_kind%newline, & & token_kind%datetime, token_kind%comma, token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_milliseconds subroutine datetime_timezone(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1987-07-05T17:45:56Z,1987-07-05T17:45:56-05:00"//new_line('a')//& & "1987-07-05T17:45:56+12:00,1987-07-05T17:45:56+13:00", & & [token_kind%datetime, token_kind%comma, token_kind%datetime, token_kind%newline, & & token_kind%datetime, token_kind%comma, token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_timezone subroutine datetime_hour_over(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "2006-01-01T24:00:00-00:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_hour_over subroutine datetime_mday_over(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "2006-01-32T00:00:00-00:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_mday_over subroutine datetime_mday_under(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "2006-01-00T00:00:00-00:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_mday_under subroutine datetime_mday_leap(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "2000-02-29T00:00:00-00:00", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_mday_leap subroutine datetime_minute_over(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "2006-01-01T00:60:00-00:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_minute_over subroutine datetime_month_over(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "2006-13-01T00:00:00-00:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_month_over subroutine datetime_month_under(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "2006-00-01T00:00:00-00:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_month_under subroutine datetime_no_leading_zeros(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1987-07-5T17:45:00.12Z", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_no_leading_zeros subroutine datetime_no_seconds(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! TOML 1.1 allows optional seconds in datetime values call check_token(error, "1987-07-05T17:45Z", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_no_seconds subroutine datetime_no_seconds_local(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! TOML 1.1 allows optional seconds - local datetime without seconds call check_token(error, "1987-07-05T17:45", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_no_seconds_local subroutine datetime_no_seconds_offset(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! TOML 1.1 allows optional seconds - datetime with offset without seconds call check_token(error, "1987-07-05T17:45+05:30", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_no_seconds_offset subroutine datetime_no_seconds_localtime(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! TOML 1.1 allows optional seconds - local time without seconds call check_token(error, "17:45", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_no_seconds_localtime subroutine datetime_no_separator(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1987-07-0517:45:00Z", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_no_separator subroutine datetime_trailing_separator(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "1987-07-05T", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_trailing_separator subroutine datetime_local_timezone(error) !> Error handling type(error_type), allocatable, intent(out) :: error call check_token(error, "00:00:00-00:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_local_timezone subroutine datetime_second_over(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Second=60 is invalid (valid range 0-59) call check_token(error, "2006-01-01T00:00:60-00:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_second_over subroutine datetime_leap_year_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Feb 29 in non-leap year (1900 is not a leap year - divisible by 100 but not 400) call check_token(error, "1900-02-29T00:00:00Z", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_leap_year_invalid subroutine datetime_leap_year_century(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! 2100 is not a leap year (divisible by 100 but not 400) ! 2000 and 2400 are leap years (divisible by 400) call check_token(error, & & "2100-02-29T00:00:00Z,2000-02-29T00:00:00Z,2400-02-29T00:00:00Z", & & [token_kind%invalid, token_kind%comma, & & token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_leap_year_century subroutine datetime_month_days_30(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! April and June have 30 days - day 31 is invalid (see datetime_month_days_30_more for Sep/Nov) call check_token(error, & & "2000-04-30T00:00:00Z,2000-04-31T00:00:00Z,2000-06-31T00:00:00Z", & & [token_kind%datetime, token_kind%comma, & & token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_month_days_30 subroutine datetime_month_days_31(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! January, March, December have 31 days (see datetime_month_days_31_more for others) call check_token(error, & & "2000-01-31T00:00:00Z,2000-03-31T00:00:00Z,2000-12-31T00:00:00Z", & & [token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_month_days_31 subroutine datetime_feb_days_invalid(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Feb has max 29 days (leap year) - Feb 30 and 31 are always invalid call check_token(error, & & "2000-02-30T00:00:00Z,2000-02-31T00:00:00Z", & & [token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_feb_days_invalid subroutine datetime_month_days_30_more(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Complete test for September and November (30-day months) - day 31 is invalid call check_token(error, & & "2000-09-30T00:00:00Z,2000-09-31T00:00:00Z,2000-11-31T00:00:00Z", & & [token_kind%datetime, token_kind%comma, & & token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_month_days_30_more subroutine datetime_month_days_31_more(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Complete test for May, July, August, October (31-day months) call check_token(error, & & "2000-05-31T00:00:00Z,2000-07-31T00:00:00Z,2000-08-31T00:00:00Z,2000-10-31T00:00:00Z", & & [token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_month_days_31_more subroutine datetime_time_boundaries(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test valid time boundaries: hour=0, hour=23, minute=0, minute=59, second=0, second=59 call check_token(error, & & "2000-01-01T00:00:00Z,2000-01-01T23:59:59Z,00:00:00,23:59:59", & & [token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_time_boundaries subroutine datetime_tz_zero_offset(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test different representations of UTC: Z, +00:00, -00:00 call check_token(error, & & "2000-01-01T12:00:00Z,2000-01-01T12:00:00+00:00,2000-01-01T12:00:00-00:00", & & [token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_tz_zero_offset subroutine datetime_milliseconds_precision(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test various millisecond precision: 1 digit, 3 digits, 6 digits call check_token(error, & & "2000-01-01T12:00:00.1Z,2000-01-01T12:00:00.123Z,2000-01-01T12:00:00.123456Z", & & [token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_milliseconds_precision subroutine datetime_year_boundaries(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test year boundary values: 0001 (minimum valid year), 9999 (maximum 4-digit year) call check_token(error, & & "0001-01-01T00:00:00Z,9999-12-31T23:59:59Z", & & [token_kind%datetime, token_kind%comma, & & token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_year_boundaries subroutine token_keypath_string(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer character(:), allocatable :: str1, str2 call new_lexer_from_string(lexer, """keypath""") call lexer%extract(toml_token(token_kind%keypath, 2, 8), str1) call lexer%extract(toml_token(token_kind%string, 1, 9), str2) call check(error, str1, str2) end subroutine token_keypath_string subroutine token_keypath_mstring(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer character(:), allocatable :: str1, str2 call new_lexer_from_string(lexer, """""""keypath""""""") call lexer%extract(toml_token(token_kind%keypath, 4, 10), str1) call lexer%extract(toml_token(token_kind%mstring, 1, 13), str2) call check(error, str1, str2) end subroutine token_keypath_mstring subroutine token_keypath_literal(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer character(:), allocatable :: str1, str2 call new_lexer_from_string(lexer, "'keypath'") call lexer%extract(toml_token(token_kind%keypath, 2, 8), str1) call lexer%extract(toml_token(token_kind%literal, 1, 9), str2) call check(error, str1, str2) end subroutine token_keypath_literal subroutine token_keypath_mliteral(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer character(:), allocatable :: str1, str2 call new_lexer_from_string(lexer, "'''keypath'''") call lexer%extract(toml_token(token_kind%keypath, 4, 10), str1) call lexer%extract(toml_token(token_kind%mliteral, 1, 13), str2) call check(error, str1, str2) end subroutine token_keypath_mliteral subroutine token_integer(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer integer(tfi) :: val1 call new_lexer_from_string(lexer, "1023,2_047,-612") call lexer%extract(toml_token(token_kind%int, 1, 4), val1) call check(error, val1, 1023_tfi) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 6, 10), val1) call check(error, val1, 2047_tfi) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 12, 16), val1) call check(error, val1, -612_tfi) if (allocated(error)) return end subroutine token_integer subroutine token_integer_binary(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer integer(tfi) :: val1 call new_lexer_from_string(lexer, "0b1001,0b0_100,-0b010") call lexer%extract(toml_token(token_kind%int, 1, 6), val1) call check(error, val1, int(b"1001", tfi)) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 8, 14), val1) call check(error, val1, int(b"0100", tfi)) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 16, 22), val1) call check(error, val1, -int(b"010", tfi)) if (allocated(error)) return end subroutine token_integer_binary subroutine token_integer_octal(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer integer(tfi) :: val1 call new_lexer_from_string(lexer, "0o1023,0o2_047,-0o612") call lexer%extract(toml_token(token_kind%int, 1, 6), val1) call check(error, val1, int(o"1023", tfi)) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 8, 14), val1) call check(error, val1, int(o"2047", tfi)) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 16, 22), val1) call check(error, val1, -int(o"612", tfi)) if (allocated(error)) return end subroutine token_integer_octal subroutine token_integer_hexadecimal(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer integer(tfi) :: val1 call new_lexer_from_string(lexer, "0x1aB1,0x0_19f,-0xF1c") call lexer%extract(toml_token(token_kind%int, 1, 6), val1) call check(error, val1, int(z"1aB1", tfi)) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 8, 14), val1) call check(error, val1, int(z"019f", tfi)) if (allocated(error)) return call lexer%extract(toml_token(token_kind%int, 16, 22), val1) call check(error, val1, -int(z"F1c", tfi)) end subroutine token_integer_hexadecimal !> Test extraction of integers using two's complement (sign bit set) subroutine token_integer_twos_complement(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer integer(tfi) :: val1 ! Test 0x8000000000000000 extracts as minimum int64 (-9223372036854775808) call new_lexer_from_string(lexer, "0x8000000000000000") call lexer%extract(toml_token(token_kind%int, 1, 18), val1) call check(error, val1, -huge(1_tfi) - 1_tfi) if (allocated(error)) return ! Test 0xFFFFFFFFFFFFFFFF extracts as -1 call new_lexer_from_string(lexer, "0xFFFFFFFFFFFFFFFF") call lexer%extract(toml_token(token_kind%int, 1, 18), val1) call check(error, val1, -1_tfi) if (allocated(error)) return ! Test 0xFFFFFFFFFFFFFFFE extracts as -2 call new_lexer_from_string(lexer, "0xFFFFFFFFFFFFFFFE") call lexer%extract(toml_token(token_kind%int, 1, 18), val1) call check(error, val1, -2_tfi) end subroutine token_integer_twos_complement subroutine token_float(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer real(tfr) :: val1 call new_lexer_from_string(lexer, "1023.0,2_000.47,-6.12e-1,1e0,1e+0,1e-0") call lexer%extract(toml_token(token_kind%float, 1, 6), val1) call check(error, val1, 1023.0_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 8, 15), val1) call check(error, val1, 2000.47_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 17, 24), val1) call check(error, val1, -6.12e-1_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 26, 28), val1) call check(error, val1, 1.0_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 30, 33), val1) call check(error, val1, 1.0_tfr) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 35, 38), val1) call check(error, val1, 1.0_tfr) if (allocated(error)) return end subroutine token_float subroutine token_float_exceptional(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer real(tfr) :: val1 call new_lexer_from_string(lexer, "nan,+nan,-nan,inf,+inf,-inf") call lexer%extract(toml_token(token_kind%float, 1, 3), val1) call check(error, val1 /= val1) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 5, 8), val1) call check(error, val1 /= val1) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 10, 13), val1) call check(error, val1 /= val1) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 15, 17), val1) call check(error, val1 > huge(val1)) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 19, 22), val1) call check(error, val1 > huge(val1)) if (allocated(error)) return call lexer%extract(toml_token(token_kind%float, 24, 27), val1) call check(error, val1 < -huge(val1)) if (allocated(error)) return end subroutine token_float_exceptional subroutine token_float_fuzz(error) !> Error handling type(error_type), allocatable, intent(out) :: error integer :: it type(toml_lexer) :: lexer real(tfr) :: val1, val2 character(128) :: buffer do it = 1, 200 call random_number(val1) call random_number(val2) val1 = (val1 - 0.5_tfr) ** nint(it * abs(val2)) write(buffer, *) val1 call new_lexer_from_string(lexer, buffer) call lexer%extract(toml_token(token_kind%float, 1, len_trim(buffer)), val2) call check(error, val2, val1, thr=2*epsilon(val1)) if (allocated(error)) exit end do end subroutine token_float_fuzz subroutine token_datetime(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer type(toml_token) :: token type(toml_datetime) :: val, ref character(*), parameter :: str = "1987-07-05 17:45:00z" call new_lexer_from_string(lexer, str) token = toml_token(token_kind%datetime, 1, 10) ref = toml_datetime(year=1987, month=7, day=5) call lexer%extract(token, val) call check(error, val == ref, & & "Extraction of '"//str(token%first:token%last)//"' failed, "//& & "expected '"//to_string(ref)//"', got '"//to_string(val)//"'") if (allocated(error)) return token = toml_token(token_kind%datetime, 1, 19) call lexer%extract(token, val) ref = toml_datetime(year=1987, month=7, day=5, hour=17, minute=45, second=0) call check(error, val == ref, & & "Extraction of '"//str(token%first:token%last)//"' failed") if (allocated(error)) return token = toml_token(token_kind%datetime, 12, 19) call lexer%extract(token, val) ref = toml_datetime(hour=17, minute=45, second=0) call check(error, val == ref, & & "Extraction of '"//str(token%first:token%last)//"' failed") if (allocated(error)) return token = toml_token(token_kind%datetime, 1, 20) call lexer%extract(token, val) ref = toml_datetime(year=1987, month=7, day=5, hour=17, minute=45, second=0, & & zone="Z") call check(error, val == ref, & & "Extraction of '"//str(token%first:token%last)//"' failed") if (allocated(error)) return end subroutine token_datetime subroutine token_bool(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer logical :: val call new_lexer_from_string(lexer, "true,false") call lexer%extract(toml_token(token_kind%bool, 1, 4), val) call check(error, val .eqv. .true.) if (allocated(error)) return call lexer%extract(toml_token(token_kind%bool, 6, 10), val) call check(error, val .eqv. .false.) if (allocated(error)) return end subroutine token_bool subroutine token_string(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer character(:), allocatable :: val call new_lexer_from_string(lexer, """\u03B4"",""\U000003B4"",""\x20"",""\u0020""") call lexer%extract(toml_token(token_kind%string, 1, 8), val) call check(error, val, "δ") if (allocated(error)) return call lexer%extract(toml_token(token_kind%string, 10, 21), val) call check(error, val, "δ") if (allocated(error)) return call lexer%extract(toml_token(token_kind%string, 23, 28), val) call check(error, val, " ") if (allocated(error)) return call lexer%extract(toml_token(token_kind%string, 30, 37), val) call check(error, val, " ") end subroutine token_string subroutine lexer_from_sequential(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_lexer) :: lexer type(toml_token) :: token type(toml_error), allocatable :: parse_error character(:), allocatable :: filename integer :: io filename = get_name() open(file=filename, newunit=io) write(io, "(a)") "abc" close(io) open(file=filename, newunit=io) call new_lexer_from_unit(lexer, io, parse_error) call move_error(error, parse_error) close(io, status="delete") if (allocated(error)) return call lexer%next(token) call check(error, token%kind, token_kind%keypath) end subroutine lexer_from_sequential subroutine check_token(error, string, expected, keypath) use tomlf_diagnostic, only : render, toml_label, toml_level use tomlf_terminal, only : toml_terminal !> Error handling type(error_type), allocatable, intent(out) :: error !> String to be parsed character(len=*), intent(in) :: string !> Expected token kind integer, intent(in) :: expected(:) !> Part of a keypath logical, intent(in) :: keypath integer :: it logical :: okay type(toml_lexer) :: lexer type(toml_token) :: token type(toml_label), allocatable :: label(:) call new_lexer_from_string(lexer, string) lexer%top = 1 lexer%stack(1)%scope = merge(1, 3, keypath) allocate(label(0)) do it = 1, size(expected) call lexer%next(token) okay = token%kind == expected(it) ! label = [label, toml_label(merge(level_info, level_error, okay), & ! & token%first, token%last, stringify(token), .not.okay)] ! msg = render(string//new_line('a'), [label(size(label))], toml_terminal(.true.)) call check(error, token%kind, expected(it), & & "Expected '"//stringify(toml_token(expected(it)))// & & "' but got '"//stringify(token)//"'")!//new_line('a')//msg) if (allocated(error)) exit end do ! msg = render(string//new_line('a'), label, toml_terminal(.true.)) ! print '(a)', msg end subroutine check_token !> Datetime timezone boundaries: Test maximum positive offset subroutine datetime_tz_max_positive(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test timezone offset +23:59 (maximum valid positive offset) call check_token(error, "1979-05-27T07:32:00+23:59", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_tz_max_positive !> Datetime timezone boundaries: Test maximum negative offset subroutine datetime_tz_max_negative(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test timezone offset -12:00 (extreme negative offset) call check_token(error, "1979-05-27T00:00:00-12:00", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_tz_max_negative !> Datetime timezone boundaries: Test hour exactly 24 should be invalid subroutine datetime_tz_hour_24(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test timezone offset +24:00 (should be invalid - hour must be < 24) call check_token(error, "1979-05-27T12:00:00+24:00", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_tz_hour_24 !> Datetime timezone boundaries: Test minute exactly 60 should be invalid subroutine datetime_tz_minute_60(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test timezone offset +12:60 (should be invalid - minute must be < 60) call check_token(error, "1979-05-27T12:00:00+12:60", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine datetime_tz_minute_60 !> Millisecond precision: Test 7+ digit fractional seconds (truncation) subroutine datetime_milliseconds_7digits(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test 7 digits - should parse but only use first 6 call check_token(error, "1979-05-27T07:32:00.9999999Z", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_milliseconds_7digits !> Millisecond precision: Test all-zero fractional seconds subroutine datetime_milliseconds_zeros(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test all zeros in fractional part call check_token(error, "1979-05-27T07:32:00.000000Z", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_milliseconds_zeros !> Millisecond precision: Test single digit fractional seconds subroutine datetime_milliseconds_1digit(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test single digit - should be equivalent to .100000 call check_token(error, "1979-05-27T07:32:00.1Z", & & [token_kind%datetime, token_kind%eof], .false.) end subroutine datetime_milliseconds_1digit !> Float exponent boundaries: Test incomplete exponent (e+ with no digits) subroutine float_exponent_incomplete_plus(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test incomplete exponent - "1e+" should be invalid call check_token(error, "1e+", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine float_exponent_incomplete_plus !> Float exponent boundaries: Test incomplete exponent (e- with no digits) subroutine float_exponent_incomplete_minus(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test incomplete exponent - "1e-" should be invalid call check_token(error, "1e-", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine float_exponent_incomplete_minus !> Float exponent boundaries: Test zero with various exponents subroutine float_zero_exponent(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test zero with exponents: 0e0, +0e0, -0e0 call check_token(error, "0e0,+0e0,-0e0", & & [token_kind%float, token_kind%comma, & & token_kind%float, token_kind%comma, & & token_kind%float, token_kind%eof], .false.) end subroutine float_zero_exponent !> Incomplete unicode escapes: Test incomplete \u escape (2 digits instead of 4) subroutine string_unicode_escape_2digits(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test incomplete \u escape with only 2 hex digits call check_token(error, '"\u00"', & & [token_kind%invalid, token_kind%eof], .false.) end subroutine string_unicode_escape_2digits !> Incomplete unicode escapes: Test incomplete \u escape (3 digits instead of 4) subroutine string_unicode_escape_3digits(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test incomplete \u escape with only 3 hex digits call check_token(error, '"\u00F"', & & [token_kind%invalid, token_kind%eof], .false.) end subroutine string_unicode_escape_3digits !> Incomplete unicode escapes: Test \u escape with 5 digits (4 valid + 1 char) subroutine string_unicode_escape_5digits(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test \u escape with 5 hex digits - should parse first 4 and treat 5th as regular char call check_token(error, '"\u00041"', & & [token_kind%string, token_kind%eof], .false.) end subroutine string_unicode_escape_5digits !> Incomplete unicode escapes: Test incomplete \U escape (4 digits instead of 8) subroutine string_unicode_escape_cap_4digits(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test incomplete \U (capital) escape with only 4 hex digits call check_token(error, '"\U0000"', & & [token_kind%invalid, token_kind%eof], .false.) end subroutine string_unicode_escape_cap_4digits !> Incomplete unicode escapes: Test incomplete \U escape (7 digits instead of 8) subroutine string_unicode_escape_cap_7digits(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test incomplete \U (capital) escape with only 7 hex digits call check_token(error, '"\U000004"', & & [token_kind%invalid, token_kind%eof], .false.) end subroutine string_unicode_escape_cap_7digits !> Integer overflow: Test hexadecimal at maximum boundary subroutine integer_hex_boundary(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test 0x7FFFFFFFFFFFFFFF (max int64 in hex) - should be valid call check_token(error, "0x7FFFFFFFFFFFFFFF", & & [token_kind%int, token_kind%eof], .false.) end subroutine integer_hex_boundary !> Integer overflow: Test hexadecimal with sign bit set (two's complement) subroutine integer_hex_overflow(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test 0x8000000000000000 (sign bit set, represents min int64 in two's complement) ! This should now be accepted and extracted as -9223372036854775808 call check_token(error, "0x8000000000000000", & & [token_kind%int, token_kind%eof], .false.) end subroutine integer_hex_overflow !> Integer overflow: Test octal at maximum boundary subroutine integer_octal_boundary(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test 0o777777777777777777777 (max int64 in octal) - should be valid call check_token(error, "0o777777777777777777777", & & [token_kind%int, token_kind%eof], .false.) end subroutine integer_octal_boundary !> Integer overflow: Test binary with maximum digits subroutine integer_binary_max_digits(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test 63-bit binary number (max positive int64) ! 0b0111111111111111111111111111111111111111111111111111111111111111 call check_token(error, "0b0111111111111111111111111111111111111111111111111111111111111111", & & [token_kind%int, token_kind%eof], .false.) end subroutine integer_binary_max_digits !> Underscore placement: Test invalid underscore immediately after base prefix subroutine integer_underscore_after_prefix(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test underscore immediately after prefix: 0x_FFF, 0o_777, 0b_101 - should all be invalid call check_token(error, "0x_FFF,0o_777,0b_101", & & [token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine integer_underscore_after_prefix !> Underscore placement: Test valid underscores in hex numbers subroutine integer_underscore_hex_valid(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test valid underscores in hex: 0xF_F_F_F, 0xCAFE_BABE - should be valid call check_token(error, "0xF_F_F_F,0xCAFE_BABE", & & [token_kind%int, token_kind%comma, token_kind%int, token_kind%eof], .false.) end subroutine integer_underscore_hex_valid !> Control characters: Test control characters in unicode escape form subroutine string_control_chars_escaped(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test control characters using unicode escapes ! \u001E (record separator), \u001D (group separator) ! Note: \u0000 (null) is rejected by verify_ucs because code > 0 check call check_token(error, '"\u001E","\u001D"', & & [token_kind%string, token_kind%comma, & & token_kind%string, token_kind%eof], .false.) end subroutine string_control_chars_escaped !> Control characters: Test invalid escape sequence \v subroutine string_invalid_escape_v(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Test \v (vertical tab) - not a standard TOML escape, should be invalid call check_token(error, '"\v"', & & [token_kind%invalid, token_kind%eof], .false.) end subroutine string_invalid_escape_v !> Negative integers: Test negative hexadecimal literals are invalid per TOML spec subroutine integer_hex_negative(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative hex values are NOT allowed per TOML spec - should be invalid ! TOML only allows negative decimal integers call check_token(error, "-0x0,-0xFF,-0xABC", & & [token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine integer_hex_negative !> Negative integers: Test negative hexadecimal at minimum boundary is invalid subroutine integer_hex_negative_boundary(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative hex is not allowed per TOML spec - should be invalid call check_token(error, "-0x8000000000000000", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_hex_negative_boundary !> Negative integers: Test negative hexadecimal overflow is invalid subroutine integer_hex_negative_overflow(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative hex is not allowed per TOML spec - should be invalid call check_token(error, "-0x8000000000000001", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_hex_negative_overflow !> Negative integers: Test negative octal literals are invalid per TOML spec subroutine integer_octal_negative(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative octal values are NOT allowed per TOML spec - should be invalid call check_token(error, "-0o0,-0o777,-0o123", & & [token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine integer_octal_negative !> Negative integers: Test negative octal at minimum boundary is invalid subroutine integer_octal_negative_boundary(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative octal is not allowed per TOML spec - should be invalid call check_token(error, "-0o1000000000000000000000", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_octal_negative_boundary !> Negative integers: Test negative octal overflow is invalid subroutine integer_octal_negative_overflow(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative octal is not allowed per TOML spec - should be invalid call check_token(error, "-0o1000000000000000000001", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_octal_negative_overflow !> Negative integers: Test negative binary literals are invalid per TOML spec subroutine integer_binary_negative(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative binary values are NOT allowed per TOML spec - should be invalid call check_token(error, "-0b0,-0b1010,-0b11", & & [token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%comma, & & token_kind%invalid, token_kind%eof], .false.) end subroutine integer_binary_negative !> Negative integers: Test negative binary at minimum boundary is invalid subroutine integer_binary_negative_boundary(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative binary is not allowed per TOML spec - should be invalid call check_token(error, "-0b1000000000000000000000000000000000000000000000000000000000000000", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_binary_negative_boundary !> Negative integers: Test negative binary overflow is invalid subroutine integer_binary_negative_overflow(error) !> Error handling type(error_type), allocatable, intent(out) :: error ! Negative binary is not allowed per TOML spec - should be invalid call check_token(error, "-0b1000000000000000000000000000000000000000000000000000000000000001", & & [token_kind%invalid, token_kind%eof], .false.) end subroutine integer_binary_negative_overflow subroutine move_error(error, parse_error) type(error_type), allocatable, intent(out) :: error type(toml_error), intent(in), optional :: parse_error if (present(parse_error)) then allocate(error) error%stat = 1 error%message = parse_error%message end if end subroutine move_error function get_name() result(filename) character(len=15) :: filename real :: val call random_number(val) write(filename, '(a, z8.8)') "toml-f-", int(val*1.0e9) end function end module tftest_lexer fortran-toml-0.5.0/test/unit/ser.f900000664000175000017500000001563715201541453017407 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tftest_ser use testdrive use tomlf_constants, only : tfi, tfr use tomlf_de, only : toml_parse, toml_loads use tomlf_ser, only : toml_dumps use tomlf_type, only : toml_table, toml_array, new_table, add_table, add_array use tomlf_build, only : set_value use tomlf_datetime, only : toml_datetime, toml_date, toml_time use tomlf_error, only : toml_error implicit none private public :: collect_ser contains !> Collect all exported unit tests subroutine collect_ser(testsuite) !> Collection of tests type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & & new_unittest("ser-simple-string", ser_simple_string), & & new_unittest("ser-integer", ser_integer), & & new_unittest("ser-float", ser_float), & & new_unittest("ser-boolean", ser_boolean), & & new_unittest("ser-array", ser_array), & & new_unittest("ser-table", ser_table), & & new_unittest("ser-roundtrip-simple", ser_roundtrip_simple)] end subroutine collect_ser !> Test serialization of simple string key-value pair subroutine ser_simple_string(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table character(:), allocatable :: output type(toml_error), allocatable :: toml_err call new_table(table) call set_value(table, "key", "value") call toml_dumps(table, output, error=toml_err) if (allocated(toml_err)) then call test_failed(error, toml_err%message) return end if call check(error, len_trim(output) > 0, "Output should not be empty") if (allocated(error)) return call check(error, index(output, "key") > 0, "Output should contain 'key'") end subroutine ser_simple_string !> Test serialization of integer values subroutine ser_integer(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table character(:), allocatable :: output type(toml_error), allocatable :: toml_err call new_table(table) call set_value(table, "int1", 42_tfi) call set_value(table, "int2", -123_tfi) call set_value(table, "int3", 0_tfi) call toml_dumps(table, output, error=toml_err) if (allocated(toml_err)) then call test_failed(error, toml_err%message) return end if call check(error, index(output, "42") > 0, "Output should contain '42'") if (allocated(error)) return call check(error, index(output, "-123") > 0, "Output should contain '-123'") end subroutine ser_integer !> Test serialization of float values subroutine ser_float(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table character(:), allocatable :: output type(toml_error), allocatable :: toml_err call new_table(table) call set_value(table, "pi", 3.14159_tfr) call set_value(table, "negative", -2.5_tfr) call set_value(table, "zero", 0.0_tfr) call toml_dumps(table, output, error=toml_err) if (allocated(toml_err)) then call test_failed(error, toml_err%message) return end if call check(error, index(output, "3.14") > 0, "Output should contain float value") end subroutine ser_float !> Test serialization of boolean values subroutine ser_boolean(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table character(:), allocatable :: output type(toml_error), allocatable :: toml_err call new_table(table) call set_value(table, "bool_true", .true.) call set_value(table, "bool_false", .false.) call toml_dumps(table, output, error=toml_err) if (allocated(toml_err)) then call test_failed(error, toml_err%message) return end if call check(error, index(output, "true") > 0, "Output should contain 'true'") if (allocated(error)) return call check(error, index(output, "false") > 0, "Output should contain 'false'") end subroutine ser_boolean !> Test serialization of simple array subroutine ser_array(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table type(toml_array), pointer :: array character(:), allocatable :: output type(toml_error), allocatable :: toml_err call new_table(table) call add_array(table, "numbers", array) call set_value(array, 1, 1_tfi) call set_value(array, 2, 2_tfi) call set_value(array, 3, 3_tfi) call toml_dumps(table, output, error=toml_err) if (allocated(toml_err)) then call test_failed(error, toml_err%message) return end if call check(error, index(output, "[") > 0, "Output should contain array bracket") end subroutine ser_array !> Test serialization of simple table subroutine ser_table(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table) :: table type(toml_table), pointer :: subtable character(:), allocatable :: output type(toml_error), allocatable :: toml_err call new_table(table) call add_table(table, "section", subtable) call set_value(subtable, "key", "value") call toml_dumps(table, output, error=toml_err) if (allocated(toml_err)) then call test_failed(error, toml_err%message) return end if call check(error, index(output, "[section]") > 0, "Output should contain table header") end subroutine ser_table !> Test round-trip serialization (parse -> serialize -> parse) subroutine ser_roundtrip_simple(error) !> Error handling type(error_type), allocatable, intent(out) :: error type(toml_table), allocatable :: table1, table2 character(:), allocatable :: output type(toml_error), allocatable :: toml_err character(*), parameter :: input = 'key = "value"'//new_line('a')//'number = 42' ! Parse input call toml_loads(table1, input, error=toml_err) if (allocated(toml_err)) then call test_failed(error, "Parse failed: "//toml_err%message) return end if ! Serialize call toml_dumps(table1, output, error=toml_err) if (allocated(toml_err)) then call test_failed(error, "Serialize failed: "//toml_err%message) return end if ! Parse again call toml_loads(table2, output, error=toml_err) if (allocated(toml_err)) then call test_failed(error, "Re-parse failed: "//toml_err%message) return end if call check(error, allocated(table2), "Round-trip should produce valid table") end subroutine ser_roundtrip_simple end module tftest_ser fortran-toml-0.5.0/test/unit/CMakeLists.txt0000664000175000017500000000162215201541453021023 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. if(NOT TARGET "test-drive::test-drive") find_package("test-drive") endif() set( test-srcs "build.f90" "lexer.f90" "main.f90" "parser.f90" "ser.f90" "sort.f90" "utils.f90" ) add_executable( tftester "${test-srcs}" ) target_link_libraries( tftester PRIVATE "${PROJECT_NAME}-lib" "test-drive::test-drive" ) add_test( "tftest" tftester ) fortran-toml-0.5.0/test/meson.build0000664000175000017500000000136115201541453017446 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Version consistency checker. subdir('version') # Unit testing subdir('unit') # Validation tests subdir('compliance') # Additional examples are compiled and tested to keep them working subdir('example-1') fortran-toml-0.5.0/test/version/0000775000175000017500000000000015201541453016770 5ustar alastairalastairfortran-toml-0.5.0/test/version/version.f900000664000175000017500000000314615201541453021001 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Check for consistency of the various version strings around program tftest_version use, intrinsic :: iso_fortran_env, only : output_unit, error_unit use tomlf implicit none integer :: length, major, minor, patch logical :: match character(len=:), allocatable :: argument, version_string call get_tomlf_version(major, minor, patch, string=version_string) if (command_argument_count() == 1) then call get_command_argument(1, length=length) allocate(character(len=length) :: argument) call get_command_argument(1, argument) match = argument == version_string write(version_string, '(*(i0:, "."))') major, minor, patch match = argument == version_string .and. match if (.not.match) then write(error_unit, '(a)') & & "Internal version and provided version do not match!" write(error_unit, '(a, ":", 1x, a)') & & "provided", argument, "internal", version_string error stop end if else write(output_unit, '(a)') version_string end if end program tftest_version fortran-toml-0.5.0/test/version/fpm.f900000664000175000017500000000373215201541453020077 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Check for consistency of the various version strings around program tftest_fpm use, intrinsic :: iso_fortran_env, only : output_unit, error_unit use tomlf use tomlf_build implicit none integer :: length character(len=:), allocatable :: argument, version_string type(toml_table), allocatable :: table integer :: unit logical :: exist, match call get_tomlf_version(string=version_string) if (abs(command_argument_count()) == 1) then call get_command_argument(1, length=length) allocate(character(len=length) :: argument) call get_command_argument(1, argument) else argument = 'fpm.toml' end if inquire(file=argument, exist=exist) if (.not.exist) then write(error_unit, '(a, ":", 1x, a)') & & "Could not find file", argument error stop end if open(newunit=unit, file=argument) call toml_parse(table, unit) close(unit) if (.not.allocated(table)) then write(error_unit, '(a, 1x, a)') & & argument, "could not be parsed, check format and/or implementation" error stop end if call get_value(table, "version", argument) match = argument == version_string if (.not.match) then write(error_unit, '(a)') & & "Internal version and provided version do not match!" write(error_unit, '(a, ":", 1x, a)') & & "provided", argument, "internal", version_string error stop end if end program tftest_fpm fortran-toml-0.5.0/test/version/meson.build0000664000175000017500000000171315201541453021134 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Check for consistency of the internal version number with the one defined # in the projects meson.build test( 'version', executable('tftest-version', files('version.f90'), dependencies: tomlf_dep), args: meson.project_version(), ) # Check if the version in the fpm.toml package file matches the internal version test( 'fpm', executable('tftest-fpm', files('fpm.f90'), dependencies: tomlf_dep), args: fpm_toml, ) fortran-toml-0.5.0/test/version/CMakeLists.txt0000664000175000017500000000241615201541453021533 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. if(POLICY CMP0110) # Required for policy change cmake_policy(SET CMP0110 NEW) endif() # Check for consistency of the internal version number with the one defined # in the projects meson.build add_executable( ${PROJECT_NAME}-test-version "version.f90" ) target_link_libraries( ${PROJECT_NAME}-test-version PRIVATE "${PROJECT_NAME}-lib" ) add_test( "${PROJECT_NAME}/version" ${PROJECT_NAME}-test-version "${PROJECT_VERSION}" ) # Check if the version in the fpm.toml package file matches the internal version add_executable( ${PROJECT_NAME}-test-fpm "fpm.f90" ) target_link_libraries( ${PROJECT_NAME}-test-fpm PRIVATE "${PROJECT_NAME}-lib" ) add_test( "${PROJECT_NAME}/fpm" ${PROJECT_NAME}-test-fpm "${fpm-toml}" ) fortran-toml-0.5.0/test/example-1/0000775000175000017500000000000015201541453017074 5ustar alastairalastairfortran-toml-0.5.0/test/example-1/app/0000775000175000017500000000000015201541453017654 5ustar alastairalastairfortran-toml-0.5.0/test/example-1/app/meson.build0000664000175000017500000000110315201541453022011 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ex1_srcs += files( 'main.f90', ) fortran-toml-0.5.0/test/example-1/app/main.f900000664000175000017500000000346215201541453021125 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Example application to show the usage of TOML-Fortran to read, modify and ! parse TOML data structures. program tomlf_example use, intrinsic :: iso_fortran_env, only : error_unit, output_unit use pkg implicit none integer :: iarg, length character(len=:), allocatable :: argument type(package_data) :: pkg_data type(error_data), allocatable :: error logical :: exist if (command_argument_count() > 0) then do iarg = 1, command_argument_count() if (allocated(argument)) deallocate(argument) call get_command_argument(iarg, length=length) allocate(character(len=length) :: argument) exist = length > 0 if (exist) then call get_command_argument(iarg, argument) write(output_unit, '(a, 1x, a, 1x, a)') & "Collecting meta data from", argument, "..." call get_package_data(pkg_data, argument, error) if (allocated(error)) then write(error_unit, '(a, 1x, a, /, a)') & & "Error while processing", argument, error%message error stop 1 end if call pkg_data%info(output_unit) end if end do end if if (allocated(argument)) deallocate(argument) end program tomlf_example fortran-toml-0.5.0/test/example-1/src/0000775000175000017500000000000015201541453017663 5ustar alastairalastairfortran-toml-0.5.0/test/example-1/src/meson.build0000664000175000017500000000112115201541453022020 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. subdir('pkg') ex1_srcs += files( 'pkg.f90', ) fortran-toml-0.5.0/test/example-1/src/pkg.f900000664000175000017500000000406715201541453020773 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Export of the public API module pkg use pkg_compatibility, only : convert, pkg_format_version use pkg_error, only : error_data, fatal_error, file_not_found_error use pkg_package, only : package_data, new_package_data use pkg_toml, only : read_config_file, toml_table, get_value implicit none private public :: get_package_data public :: package_data, error_data contains !> Obtain package meta data from an configuation file subroutine get_package_data(pkg_data, pkg_file, error) !> Parsed package meta data type(package_data), intent(out) :: pkg_data !> Name of the package configuration file character(len=*), intent(in) :: pkg_file !> Error status of the operation type(error_data), allocatable, intent(out) :: error type(toml_table), allocatable :: table integer :: format_version call read_config_file(table, pkg_file, error) if (allocated(error)) return if (.not.allocated(table)) then call fatal_error(error, "Unclassified error while reading: '"//pkg_file//"'") return end if call get_value(table, "pkg_version", format_version, pkg_format_version) if (format_version > pkg_format_version) then call fatal_error(error, "This packager does not support the provided package file format") return end if if (format_version < pkg_format_version) then call convert(table, format_version, error) if (allocated(error)) return end if call new_package_data(pkg_data, table, error) end subroutine get_package_data end module pkg fortran-toml-0.5.0/test/example-1/src/pkg/0000775000175000017500000000000015201541453020444 5ustar alastairalastairfortran-toml-0.5.0/test/example-1/src/pkg/compatibility.f900000664000175000017500000000473715201541453023650 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Copyright (C) 2019-2020 Sebastian Ehlert ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation to ensure compatibility with older versions of the packaging ! configuration format module pkg_compatibility use pkg_error, only : error_data, fatal_error, file_not_found_error use pkg_package, only : package_data, new_package_data use tomlf, only : toml_table, toml_stat, get_value use tomlf_type, only : toml_value implicit none private public :: convert, pkg_format_version !> Current package format version integer, parameter :: pkg_format_version = 2 contains !> Attempt to convert package meta data to a newer version subroutine convert(table, format_version, error) !> TOML data structure representing the package meta data type(toml_table), intent(inout) :: table !> Current version of the format integer, intent(inout) :: format_version !> Error status of the operation type(error_data), allocatable, intent(out) :: error type(toml_table), pointer :: child type(toml_table), allocatable :: tmp class(toml_value), allocatable :: node integer :: stat if (format_version == 1) then ! `requirements` has been renamed to `dependencies`, ! the format inside the table remains the same call get_value(table, "requirements", child, requested=.false.) if (associated(child)) then ! deepcopy the complete dependencies table tmp = child tmp%key = "dependencies" ! delete the old entry call table%delete("requirements") ! move the allocation of the adjusted copy into the table:w call move_alloc(tmp, node) call table%push_back(node, stat) if (stat /= toml_stat%success) then call fatal_error(error, "Conversion failed, could migrate requirements table to dependencies table") return end if end if format_version = format_version + 1 end if end subroutine convert end module pkg_compatibility fortran-toml-0.5.0/test/example-1/src/pkg/package.f900000664000175000017500000001317015201541453022361 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of the package meta data storage module pkg_package use pkg_dependency, only : dependency_data, new_dependency use pkg_error, only : error_data, fatal_error use pkg_source, only : source_data, new_source use tomlf use tomlf_type, only : len implicit none private public :: package_data, new_package_data !> Central package meta data type :: package_data !> Name of the package character(len=:), allocatable :: name !> Version of the package character(len=:), allocatable :: version !> License character(len=:), allocatable :: license !> Sources to build the package type(source_data), allocatable :: sources(:) !> Dependencies required by the package type(dependency_data), allocatable :: dependencies(:) contains !> Produce informative printout procedure :: info end type package_data contains !> Initialize package data from a TOML data structure subroutine new_package_data(self, table, error) !> Instance of the package data type(package_data), intent(out) :: self !> TOML data structure, might get modified in the process type(toml_table), intent(inout) :: table !> Error status of the operation type(error_data), allocatable, intent(out) :: error type(toml_table), pointer :: child, node type(toml_array), pointer :: children type(toml_key), allocatable :: list(:) character(len=:), allocatable :: license integer :: stat, idep, isrc, ilic, nlic call get_value(table, "name", self%name) if (.not.allocated(self%name)) then call fatal_error(error, "Package name is not provided") end if call get_value(table, "version", self%version, "0.0.0") call get_value(table, "license", children, requested=.false.) if (associated(children)) then nlic = len(children) if (nlic < 1) then call fatal_error(error, "License field requires at least one entry or must be omitted") return end if do ilic = 1, nlic call get_value(children, ilic, license) if (.not.allocated(license)) then call fatal_error(error, "Element of license array is not a string value") exit end if if (.not.allocated(self%license)) then call move_alloc(license, self%license) else self%license = self%license // ' or ' // license end if end do if (allocated(error)) return else call get_value(table, "license", self%license, "unknown") end if call get_value(table, "dependencies", child, requested=.false.) if (associated(child)) then call child%get_keys(list) allocate(self%dependencies(size(list))) do idep = 1, size(list) call get_value(child, list(idep)%key, node, stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "Dependency "//list(idep)%key//" must be a table entry") exit end if call new_dependency(self%dependencies(idep), node, error) if (allocated(error)) exit end do if (allocated(error)) return end if call get_value(table, "source", child, requested=.false.) if (associated(child)) then allocate(self%sources(1)) call new_source(self%sources(1), child, error) else call get_value(table, "source", children, requested=.false.) if (.not.associated(children)) then call fatal_error(error, "Requires at least one source table") return end if if (.not.is_array_of_tables(children)) then call fatal_error(error, "Sources must be provided as table or array of tables") return end if allocate(self%sources(len(children))) do isrc = 1, len(children) call get_value(children, isrc, node, stat=stat) if (stat /= toml_stat%success) then call fatal_error(error, "Could not retrieve sources from array entry") exit end if call new_source(self%sources(isrc), node, error) if (allocated(error)) exit end do end if if (allocated(error)) return end subroutine new_package_data !> Write information on the package data subroutine info(self, unit) !> Instance of the package data class(package_data), intent(in) :: self !> Unit for IO integer, intent(in) :: unit integer :: idep, isrc write(unit, '(a, t30, a)') "Package name", self%name write(unit, '(a, t30, a)') "Package version", self%version write(unit, '(a, t30, a)') "Package license", self%license write(unit, '(a, t30, i0)') "Sources collected", size(self%sources) if (allocated(self%dependencies)) then write(unit, '(a, t30, i0)') "Dependencies collected", size(self%dependencies) end if write(unit, '(a)') "Source targets:" do isrc = 1, size(self%sources) call self%sources(isrc)%info(unit) end do if (allocated(self%dependencies)) then write(unit, '(a)') "Dependencies:" do idep = 1, size(self%dependencies) call self%dependencies(idep)%info(unit) end do end if end subroutine info end module pkg_package fortran-toml-0.5.0/test/example-1/src/pkg/meson.build0000664000175000017500000000123615201541453022610 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ex1_srcs += files( 'compatibility.f90', 'dependency.f90', 'error.f90', 'package.f90', 'source.f90', 'toml.f90', ) fortran-toml-0.5.0/test/example-1/src/pkg/dependency.f900000664000175000017500000000534715201541453023113 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of dependencies to other packages module pkg_dependency use pkg_error, only : error_data, fatal_error use tomlf implicit none private public :: dependency_data, new_dependency !> Dependency for a package type :: dependency_data !> Name of the dependency character(len=:), allocatable :: name !> Version constraints character(len=:), allocatable :: version !> Optional variant information in case multiple vendors are available or ! multiple versions of the same dependency are provided character(len=:), allocatable :: variant !> Optional git URL to and build the requested dependency character(len=:), allocatable :: git contains !> Produce informative printout procedure :: info end type dependency_data contains !> Create a new dependency from a TOML data structure subroutine new_dependency(self, table, error) !> Instance of the dependency data type(dependency_data), intent(out) :: self !> TOML data structure, might get modified in the process type(toml_table), intent(inout) :: table !> Error status of the operation type(error_data), allocatable, intent(out) :: error call table%get_key(self%name) call get_value(table, "version", self%version, "*") call get_value(table, "variant", self%variant) call get_value(table, "git", self%git) if (allocated(self%variant) .and. allocated(self%git)) then call fatal_error(error, "Cannot have both variant and git field for a dependency") end if end subroutine new_dependency !> Write information on the package data subroutine info(self, unit) !> Instance of the package data class(dependency_data), intent(in) :: self !> Unit for IO integer, intent(in) :: unit write(unit, '(" - ", a, t30, a)') "Dependency name", self%name if (self%version /= "*") then write(unit, '(3x, a, t30, a)') "Required version", self%version end if if (allocated(self%git)) then write(unit, '(3x, a, t30, a)') "Git URL", self%git end if if (allocated(self%variant)) then write(unit, '(3x, a, t30, a)') "Variant used", self%variant end if end subroutine info end module pkg_dependency fortran-toml-0.5.0/test/example-1/src/pkg/source.f900000664000175000017500000000614415201541453022271 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of a package source target module pkg_source use pkg_error, only : error_data, fatal_error use tomlf implicit none private public :: source_data, new_source !> Package source data, knows how to retrieve and verify itself type :: source_data !> URL to the package source character(len=:), allocatable :: url !> Path to version control repository character(len=:), allocatable :: git !> SHA-256 hash to verify artifacts downloaded from URL entry character(len=:), allocatable :: sha256 !> Relative path in the build directory character(len=:), allocatable :: folder contains !> Produce informative printout procedure :: info end type source_data contains !> Create new source data from a TOML data structure subroutine new_source(self, table, error) !> Instance of the dependency data type(source_data), intent(out) :: self !> TOML data structure, might get modified in the process type(toml_table), intent(inout) :: table !> Error status of the operation type(error_data), allocatable, intent(out) :: error call get_value(table, "url", self%url) call get_value(table, "sha256", self%sha256) call get_value(table, "git", self%git) if (.not.(allocated(self%git).or.allocated(self%url))) then call fatal_error(error, "Needs at least one of url or git methods to retrieve source targets") return end if if (allocated(self%git) .and. allocated(self%url)) then call fatal_error(error, "Cannot have both url and git field present for source target") return end if if (.not.allocated(self%sha256) .and. allocated(self%url)) then call fatal_error(error, "SHA256 checksum is required for URL source targets") return end if if (allocated(self%git) .and. allocated(self%sha256)) then call fatal_error(error, "SHA256 hash cannot be used with git sources") return end if call get_value(table, "folder", self%folder, ".") end subroutine new_source !> Write information on the package data subroutine info(self, unit) !> Instance of the package data class(source_data), intent(in) :: self !> Unit for IO integer, intent(in) :: unit if (allocated(self%git)) then write(unit, '(" - ", a, t30, a)') "Git URL", self%git end if if (allocated(self%url)) then write(unit, '(" - ", a, t30, a)') "Source URL", self%url end if if (self%folder /= ".") then write(unit, '(3x, a, t30, a)') "Relative path", self%folder end if end subroutine info end module pkg_source fortran-toml-0.5.0/test/example-1/src/pkg/toml.f900000664000175000017500000000347215201541453021745 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Processing of the configuration file ! ! Reexports the used parts of the TOML-Fortran API module pkg_toml use pkg_error, only : error_data, fatal_error, file_not_found_error use tomlf, only : toml_table, toml_error, toml_parse, toml_stat, & & set_value, get_value implicit none private public :: read_config_file public :: toml_table, toml_stat, set_value, get_value contains !> Process the configuration file to a TOML data structure subroutine read_config_file(table, config, error) !> TOML data structure type(toml_table), allocatable, intent(out) :: table !> Name of the package configuration file character(len=*), intent(in) :: config !> Error status of the operation type(error_data), allocatable, intent(out) :: error type(toml_error), allocatable :: parse_error integer :: unit logical :: exist inquire(file=config, exist=exist) if (.not.exist) then call file_not_found_error(error, config) return end if open(file=config, newunit=unit, status='old') call toml_parse(table, unit, parse_error) close(unit) if (allocated(parse_error)) then allocate(error) call move_alloc(parse_error%message, error%message) return end if end subroutine read_config_file end module pkg_toml fortran-toml-0.5.0/test/example-1/src/pkg/error.f900000664000175000017500000000321515201541453022116 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of basic error handling module pkg_error implicit none private public :: error_data public :: fatal_error, file_not_found_error !> Data type defining an error type :: error_data !> Error message character(len=:), allocatable :: message end type error_data contains !> Generic fatal runtime error subroutine fatal_error(error, message) !> Instance of the error data type(error_data), allocatable, intent(out) :: error !> Name of the missing file character(len=*), intent(in) :: message allocate(error) error%message = message end subroutine fatal_error !> Error created when a file is missing or not found subroutine file_not_found_error(error, file_name) !> Instance of the error data type(error_data), allocatable, intent(out) :: error !> Name of the missing file character(len=*), intent(in) :: file_name character(len=:), allocatable :: message message = "'"//file_name//"' could not be found, check if the file exists" call move_alloc(message, error%message) end subroutine file_not_found_error end module pkg_error fortran-toml-0.5.0/test/example-1/package-1.toml0000664000175000017500000000225415201541453021525 0ustar alastairalastair# Package file example oriented at the conda-build meta.yaml for the DFTB+ project name = "dftbplus" version = "20.1" license = "LGPL-3.0-or-later" # Requests an older version of this made-up package format to trigger a converion # on the TOML datastructure to match the actual format pkg_version = 1 # This table will be moved to dependencies [requirements] lapack.version = "*" blas.version = "*" [requirements.mpi] version = "*" variant = "openmpi" [requirements.scalapack] version = "*" # Source can either be a single table or an array of tables, the packager support # both formats by checking for the type of the respective source node [[source]] url = "https://github.com/dftbplus/dftbplus/archive/20.1.tar.gz" sha256 = "a155ca927c804234587c61c4938d154f31578c816b0ce20eaee3b5d7e39d91dc" [[source]] url = "https://github.com/dftbplus/mpifx/archive/dftbplus-20.1.tar.gz" sha256 = "321808573bc7f7719a34516723bd9514ddb6db508a74b572a52794f7bc864f93" folder = "external/mpifx/origin" [[source]] url = "https://github.com/dftbplus/scalapackfx/archive/dftbplus-20.1.tar.gz" sha256 = "23797a2ac8c495a4bd9cc9445f14a8aca7eee0ce585f86b9e575b1e2a4b68cb6" folder = "external/scalapackfx/origin" fortran-toml-0.5.0/test/example-1/meson.build0000664000175000017500000000147115201541453021241 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ex1_srcs = [] subdir('app') subdir('src') example1 = executable( 'example', sources: ex1_srcs, dependencies: tomlf_dep, ) if os != 'windows' test('example-1.1', example1, args: files('package-1.toml')) test('example-1.2', example1, args: files('package-2.toml')) endif fortran-toml-0.5.0/test/example-1/package-2.toml0000664000175000017500000000106715201541453021527 0ustar alastairalastair# A simple package file oriented on the fpm format for packaging name = "simple" # Version numbers can be complicated, this is using a semantic version after # an version epoch (maybe due to a versioning style change) and an build number version = "1:1.2.0-1" # In case of dual licensed packages we can either provide an array or use the # an logical or between the SPDX identifiers, the packager will allow arrays # for convenience and convert to SPDX identifiers with or internally license = ["Apache-2.0", "MIT"] [source] git = "https://github.com/toml-f/toml-f" fortran-toml-0.5.0/test/example-1/README.md0000664000175000017500000000163215201541453020355 0ustar alastairalastair## Using TOML-Fortran with the Fortran package manager The Fortran package manager [`fpm`](https://github.com/fortran-lang/fpm) can be used to declare TOML-Fortran as dependency for a project, just add ```toml [dependencies] toml-f = { git = "https://github.com/toml-f/toml-f" } ``` For more details checkout the packaging information in the `fpm` repository. This example project will build a simple executable to read and process TOML documents using the TOML-Fortran dependency provided from the project root, serving both as example and a simple integration test. Test this example by running it from `fpm` on one of the provided example TOML documents: ``` fpm run -- package-1.toml fpm run -- package-2.toml ``` Note, this is an example project with a made-up package format, for the mere reason of demonstrating functionality, therefore, do not expect all design choices in this example to be sensible ones. fortran-toml-0.5.0/test/example-1/fpm.toml0000664000175000017500000000022615201541453020553 0ustar alastairalastairname = "toml-f-dep" version = "0.0.0" [dependencies] toml-f = {path = "../.."} [[executable]] name = "example" source-dir = "app" main = "main.f90" fortran-toml-0.5.0/test/example-1/.gitignore0000664000175000017500000000001115201541453021054 0ustar alastairalastair/build*/ fortran-toml-0.5.0/test/CMakeLists.txt0000664000175000017500000000126715201541453020051 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Version consistency checker. add_subdirectory("version") # Unit testing add_subdirectory("unit") # Validation tests add_subdirectory("compliance") fortran-toml-0.5.0/test/compliance/0000775000175000017500000000000015201541453017415 5ustar alastairalastairfortran-toml-0.5.0/test/compliance/json_parser.f900000664000175000017500000002164115201541453022266 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tjson_parser use tomlf_constants, only : tfc, tfi, tfr, toml_type use tomlf_datetime, only : toml_datetime use tomlf_de_context, only : toml_context use tjson_lexer, only : json_lexer, new_lexer_from_string, new_lexer_from_unit, & & new_lexer_from_file use tomlf_de_parser, only : parse, toml_parser_config use tomlf_diagnostic, only : toml_level use tomlf_build, only : get_value use tomlf_error, only : toml_error use tomlf_type, only : toml_table, toml_value, cast_to_table, & & toml_visitor, toml_array, toml_keyval, toml_key, len implicit none private public :: json_load, json_loads public :: toml_context, toml_parser_config, toml_level !> Load a TOML data structure from the provided source interface json_load module procedure :: json_load_file module procedure :: json_load_unit end interface json_load !> Load a TOML data structure from a string interface json_loads module procedure :: json_load_string end interface json_loads !> Implement pruning of annotated values as visitor type, extends(toml_visitor) :: json_prune contains !> Traverse the AST and prune all annotated values procedure :: visit end type json_prune contains !> Load TOML data structure from file subroutine json_load_file(object, filename, config, context, error) !> Instance of the TOML data structure, not allocated in case of error class(toml_value), allocatable, intent(out) :: object !> Name of the file to load character(*, tfc), intent(in) :: filename !> Configuration for the parser type(toml_parser_config), intent(in), optional :: config !> Context tracking the origin of the data structure to allow rich reports type(toml_context), intent(out), optional :: context !> Error handling, provides detailed diagnostic in case of error type(toml_error), allocatable, intent(out), optional :: error type(json_lexer) :: lexer type(toml_error), allocatable :: error_ type(toml_table), allocatable :: table call new_lexer_from_file(lexer, filename, error_) if (.not.allocated(error_)) then call parse(lexer, table, config, context, error) if (allocated(table)) call prune(object, table) else if (present(error)) call move_alloc(error_, error) end if end subroutine json_load_file !> Load TOML data structure from unit subroutine json_load_unit(object, io, config, context, error) !> Instance of the TOML data structure, not allocated in case of error class(toml_value), allocatable, intent(out) :: object !> Unit to read from integer, intent(in) :: io !> Configuration for the parser type(toml_parser_config), intent(in), optional :: config !> Context tracking the origin of the data structure to allow rich reports type(toml_context), intent(out), optional :: context !> Error handling, provides detailed diagnostic in case of error type(toml_error), allocatable, intent(out), optional :: error type(json_lexer) :: lexer type(toml_error), allocatable :: error_ type(toml_table), allocatable :: table call new_lexer_from_unit(lexer, io, error_) if (.not.allocated(error_)) then call parse(lexer, table, config, context, error) if (allocated(table)) call prune(object, table) else if (present(error)) call move_alloc(error_, error) end if end subroutine json_load_unit !> Load TOML data structure from string subroutine json_load_string(object, string, config, context, error) !> Instance of the TOML data structure, not allocated in case of error class(toml_value), allocatable, intent(out) :: object !> String containing TOML document character(*, tfc), intent(in) :: string !> Configuration for the parser type(toml_parser_config), intent(in), optional :: config !> Context tracking the origin of the data structure to allow rich reports type(toml_context), intent(out), optional :: context !> Error handling, provides detailed diagnostic in case of error type(toml_error), allocatable, intent(out), optional :: error type(json_lexer) :: lexer type(toml_table), allocatable :: table call new_lexer_from_string(lexer, string) call parse(lexer, table, config, context, error) if (allocated(table)) call prune(object, table) end subroutine json_load_string !> Prune the artificial root table inserted by the lexer subroutine prune(object, table) !> Instance of the TOML data structure, not allocated in case of error class(toml_value), allocatable, intent(inout) :: object !> Instance of the TOML data structure, not allocated in case of error type(toml_table), allocatable, intent(inout) :: table type(json_prune) :: pruner call table%pop("_", object) if (allocated(object)) call object%accept(pruner) end subroutine prune !> Visit a TOML value subroutine visit(self, val) !> Instance of the JSON pruner class(json_prune), intent(inout) :: self !> TOML value to visit class(toml_value), intent(inout) :: val select type(val) class is(toml_array) call visit_array(self, val) class is(toml_table) call visit_table(self, val) end select end subroutine visit !> Visit a TOML array subroutine visit_array(visitor, array) !> Instance of the JSON pruner class(json_prune), intent(inout) :: visitor !> TOML value to visit type(toml_array), intent(inout) :: array class(toml_value), allocatable :: val, tmp character(kind=tfc, len=:), allocatable :: str type(toml_key), allocatable :: vt(:) integer :: i, n, stat n = len(array) do i = 1, n call array%shift(val) select type(val) class default call val%accept(visitor) class is(toml_table) call val%get_keys(vt) if (val%has_key("type") .and. val%has_key("value") .and. size(vt)==2) then call get_value(val, "type", str) call prune_value(tmp, val, str) call val%destroy call tmp%accept(visitor) call array%push_back(tmp, stat) cycle else call val%accept(visitor) end if end select call array%push_back(val, stat) end do end subroutine visit_array !> Visit a TOML table subroutine visit_table(visitor, table) !> Instance of the JSON pruner class(json_prune), intent(inout) :: visitor !> TOML table to visit type(toml_table), intent(inout) :: table class(toml_value), pointer :: ptr class(toml_value), allocatable :: val character(kind=tfc, len=:), allocatable :: str type(toml_key), allocatable :: list(:), vt(:) integer :: i, n, stat call table%get_keys(list) n = size(list, 1) do i = 1, n call table%get(list(i)%key, ptr) select type(ptr) class default call ptr%accept(visitor) class is(toml_table) call ptr%get_keys(vt) if (ptr%has_key("type") .and. ptr%has_key("value") .and. size(vt)==2) then call get_value(ptr, "type", str) call prune_value(val, ptr, str) call val%accept(visitor) call table%delete(list(i)%key) call table%push_back(val, stat) else call ptr%accept(visitor) end if end select end do end subroutine visit_table subroutine prune_value(val, table, str) !> Actual TOML value class(toml_value), allocatable, intent(out) :: val !> TOML table to prune type(toml_table), intent(inout) :: table !> Value kind character(kind=tfc, len=*), intent(in) :: str class(toml_value), pointer :: ptr character(:, tfc), pointer :: sval integer :: stat type(toml_datetime) :: dval integer(tfi) :: ival real(tfr) :: fval call table%get("value", ptr) allocate(val, source=ptr) if (allocated(table%key)) then val%key = table%key else deallocate(val%key) end if select type(val) class is(toml_keyval) call val%get(sval) select case(str) case("date", "time", "datetime", "date-local", "time-local", "datetime-local") dval = toml_datetime(sval) call val%set(dval) case("bool") call val%set(sval == "true") case("integer") read(sval, *, iostat=stat) ival if (stat == 0) then call val%set(ival) end if case("float") read(sval, *, iostat=stat) fval if (stat == 0) then call val%set(fval) end if end select end select end subroutine prune_value end module tjson_parser fortran-toml-0.5.0/test/compliance/meson.build0000664000175000017500000000710515201541453021562 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Serializer from TOML to a specific JSON format suitable for the validator suite toml2json_exe = executable( 'toml2json', sources: files( 'json_ser.f90', 'toml2json.f90', ), dependencies: tomlf_dep, ) # Deserializer for a specific JSON format suitable for the validator suite json2toml_exe = executable( 'json2toml', sources: files( 'json_lexer.f90', 'json_parser.f90', 'json2toml.f90', ), dependencies: tomlf_dep, ) # We will run a TOML decoder validator, in case we find a go installation go_exe = find_program('go', required: false) if not meson.is_subproject() and go_exe.found() toml_test_prj = subproject('toml-test-1.1.0') toml_test_exe = toml_test_prj.get_variable('toml_test_exe') validator_testdir = toml_test_prj.get_variable('compliance_tests') valid_tests = toml_test_prj.get_variable('valid_tests') invalid_tests = toml_test_prj.get_variable('invalid_tests') if not toml_test_exe.found() subdir_done() endif valid_failing = [ 'valid/key/escapes', 'valid/string/escape-tricky', 'valid/string/escapes', 'valid/string/multiline', ] foreach t : valid_tests if not valid_failing.contains(t) test( t, toml_test_exe, args: ['-testdir', validator_testdir, '-run', t, '--', toml2json_exe], suite: 'decoder', ) else benchmark( t, toml_test_exe, args: ['-testdir', validator_testdir, '-run', t, '--', toml2json_exe], suite: 'decoder', ) endif endforeach invalid_failing = [ 'invalid/control/bare-cr', 'invalid/control/comment-cr', 'invalid/control/comment-del', 'invalid/control/comment-lf', 'invalid/control/comment-null', 'invalid/control/comment-us', 'invalid/encoding/bad-utf8-in-comment', 'invalid/encoding/bad-utf8-in-string', 'invalid/string/basic-out-of-range-unicode-escape-2', 'invalid/table/append-with-dotted-keys-1', 'invalid/table/append-with-dotted-keys-2', ] foreach t : invalid_tests if not invalid_failing.contains(t) test( t, toml_test_exe, args: ['-testdir', validator_testdir, '-run', t, '--', toml2json_exe], suite: 'decoder', ) else benchmark( t, toml_test_exe, args: ['-testdir', validator_testdir, '-run', t, '--', toml2json_exe], suite: 'decoder', ) endif endforeach encoder_failing = [ 'valid/array/mixed-string-table', 'valid/inline-table/end-in-bool', 'valid/key/escapes', 'valid/key/special-chars', 'valid/string/escapes', 'valid/string/multiline-quotes', ] foreach t : valid_tests if not encoder_failing.contains(t) test( t, toml_test_exe, args: ['-encoder', '-testdir', validator_testdir, '-run', t, '--', json2toml_exe], suite: 'encoder', ) else benchmark( t, toml_test_exe, args: ['-encoder', '-testdir', validator_testdir, '-run', t, '--', json2toml_exe], suite: 'encoder', ) endif endforeach endif fortran-toml-0.5.0/test/compliance/json_ser.f900000664000175000017500000001737615201541453021575 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Implementation of a serializer for TOML values to JSON, used for testing only. module tjson_ser use tomlf_constants use tomlf_datetime use tomlf_type, only : toml_value, toml_visitor, toml_key, toml_table, & & toml_array, toml_keyval, is_array_of_tables, len implicit none private public :: json_serializer !> Serializer to produduce a JSON document from a TOML datastructure type, extends(toml_visitor) :: json_serializer !> Unit for output integer :: unit = tfout !> Indentation character(len=:), allocatable :: indentation !> Current depth in the tree integer :: depth = 0 contains !> Visit a TOML value procedure :: visit end type json_serializer contains !> Visit a TOML value subroutine visit(self, val) !> Instance of the JSON serializer class(json_serializer), intent(inout) :: self !> TOML value to visit class(toml_value), intent(inout) :: val select type(val) class is(toml_keyval) call visit_keyval(self, val) class is(toml_array) call visit_array(self, val) class is(toml_table) call visit_table(self, val) end select end subroutine visit !> Visit a TOML key-value pair subroutine visit_keyval(visitor, keyval) !> Instance of the JSON serializer class(json_serializer), intent(inout) :: visitor !> TOML value to visit type(toml_keyval), intent(inout) :: keyval character(kind=tfc, len=:), allocatable :: str, key character(kind=tfc, len=:), pointer :: sdummy type(toml_datetime), pointer :: ts integer(tfi), pointer :: idummy real(tfr), pointer :: fdummy logical, pointer :: ldummy call indent(visitor) if (allocated(keyval%key)) then call escape_string(keyval%key, key) write(visitor%unit, '("""", a, """: ")', advance='no') key end if select case(keyval%get_type()) case default write(visitor%unit, '(a)', advance='no') & & '{}' case(toml_type%string) call keyval%get(sdummy) call escape_string(sdummy, str) write(visitor%unit, '(a,a,a)', advance='no') & & '{"type": "string", "value": "', str, '"}' case(toml_type%boolean) call keyval%get(ldummy) if (ldummy) then write(visitor%unit, '(a)', advance='no') & & '{"type": "bool", "value": "true"}' else write(visitor%unit, '(a)', advance='no') & & '{"type": "bool", "value": "false"}' end if case(toml_type%int) call keyval%get(idummy) write(visitor%unit, '(a,i0,a)', advance='no') & & '{"type": "integer", "value": "', idummy, '"}' case(toml_type%float) call keyval%get(fdummy) write(visitor%unit, '(a)', advance='no') & & '{"type": "float", "value": "' if (fdummy > huge(fdummy)) then write(visitor%unit, '(a)', advance='no') "+inf" else if (fdummy < -huge(fdummy)) then write(visitor%unit, '(a)', advance='no') "-inf" else if (fdummy /= fdummy) then write(visitor%unit, '(a)', advance='no') "nan" else write(visitor%unit, '(g0)', advance='no') fdummy end if write(visitor%unit, '(a)', advance='no') '"}' case(toml_type%datetime) call keyval%get(ts) write(visitor%unit, '(a)', advance='no') '{"type": "' if (has_date(ts)) write(visitor%unit, '(a)', advance='no') 'date' if (has_time(ts)) then write(visitor%unit, '(a)', advance='no') 'time' if (.not.allocated(ts%time%zone)) then write(visitor%unit, '(a)', advance='no') '-local' end if else write(visitor%unit, '(a)', advance='no') '-local' end if str = to_string(ts) write(visitor%unit, '(a,a,a)', advance='no') & & '", "value": "', str, '"}' end select end subroutine visit_keyval !> Visit a TOML array subroutine visit_array(visitor, array) !> Instance of the JSON serializer class(json_serializer), intent(inout) :: visitor !> TOML value to visit type(toml_array), intent(inout) :: array class(toml_value), pointer :: ptr character(kind=tfc, len=:), allocatable :: key integer :: i, n call indent(visitor) if (allocated(array%key)) then call escape_string(array%key, key) write(visitor%unit, '("""",a,""": ")', advance='no') key end if write(visitor%unit, '("[")', advance='no') visitor%depth = visitor%depth + 1 n = len(array) do i = 1, n call array%get(i, ptr) call ptr%accept(visitor) if (i /= n) write(visitor%unit, '(",")', advance='no') end do visitor%depth = visitor%depth - 1 call indent(visitor) write(visitor%unit, '("]")', advance='no') end subroutine visit_array !> Visit a TOML table subroutine visit_table(visitor, table) !> Instance of the JSON serializer class(json_serializer), intent(inout) :: visitor !> TOML table to visit type(toml_table), intent(inout) :: table class(toml_value), pointer :: ptr type(toml_key), allocatable :: list(:) character(kind=tfc, len=:), allocatable :: key integer :: i, n call indent(visitor) if (allocated(table%key)) then call escape_string(table%key, key) write(visitor%unit, '("""",a,""": ")', advance='no') key end if write(visitor%unit, '("{")', advance='no') visitor%depth = visitor%depth + 1 call table%get_keys(list) n = size(list, 1) do i = 1, n call table%get(list(i)%key, ptr) call ptr%accept(visitor) if (i /= n) write(visitor%unit, '(",")', advance='no') end do visitor%depth = visitor%depth - 1 call indent(visitor) if (visitor%depth == 0) then if (allocated(visitor%indentation)) write(visitor%unit, '(a)') write(visitor%unit, '("}")') else write(visitor%unit, '("}")', advance='no') endif end subroutine visit_table !> Produce indentations for emitted JSON documents subroutine indent(self) !> Instance of the JSON serializer class(json_serializer), intent(inout) :: self integer :: i ! PGI internal compiler error in NVHPC 20.7 and 20.9 with ! write(self%unit, '(/, a)', advance='no') repeat(self%indentation, self%depth) ! causes: NVFORTRAN-F-0000-Internal compiler error. Errors in Lowering 16 if (allocated(self%indentation) .and. self%depth > 0) then write(self%unit, '(/)', advance='no') do i = 1, self%depth write(self%unit, '(a)', advance='no') self%indentation end do end if end subroutine indent !> Transform a TOML raw value to a JSON compatible escaped string subroutine escape_string(raw, escaped) !> Raw value of TOML value character(len=*), intent(in) :: raw !> JSON compatible escaped string character(len=:), allocatable, intent(out) :: escaped integer :: i escaped = '' do i = 1, len(raw) select case(raw(i:i)) case default; escaped = escaped // raw(i:i) case('\'); escaped = escaped // '\\' case('"'); escaped = escaped // '\"' case(TOML_NEWLINE); escaped = escaped // '\n' case(TOML_FORMFEED); escaped = escaped // '\f' case(TOML_CARRIAGE_RETURN); escaped = escaped // '\r' case(TOML_TABULATOR); escaped = escaped // '\t' case(TOML_BACKSPACE); escaped = escaped // '\b' end select end do end subroutine escape_string end module tjson_ser fortran-toml-0.5.0/test/compliance/json2toml.f900000664000175000017500000000403115201541453021662 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Example executable to read and emit a TOML document program json2toml use, intrinsic :: iso_fortran_env, only : input_unit, output_unit use tomlf use tjson_parser implicit none integer :: iarg, length character(len=:), allocatable :: argument class(toml_value), allocatable :: object type(toml_error), allocatable :: error logical :: exist if (command_argument_count() > 0) then do iarg = 1, command_argument_count() if (allocated(argument)) deallocate(argument) call get_command_argument(iarg, length=length) allocate(character(len=length) :: argument) exist = length > 0 if (exist) then call get_command_argument(iarg, argument) inquire(file=argument, exist=exist) end if if (exist) then if (allocated(object)) deallocate(object) call json_load(object, argument, error=error) if (allocated(error)) print '(a)', error%message if (allocated(object)) then call toml_dump(object, output_unit, error) call object%destroy end if end if end do else call json_load(object, input_unit, error=error) if (allocated(error)) print '(a)', error%message if (allocated(object)) then call toml_dump(object, output_unit, error) call object%destroy else error stop end if end if if (allocated(argument)) deallocate(argument) end program json2toml fortran-toml-0.5.0/test/compliance/toml2json.f900000664000175000017500000000430315201541453021664 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. !> Example executable to read and emit a TOML document program toml2json use, intrinsic :: iso_fortran_env, only : input_unit, error_unit use tomlf use tjson_ser implicit none integer :: iarg, length character(len=:), allocatable :: argument type(toml_table), allocatable :: table type(toml_error), allocatable :: error type(json_serializer) :: ser integer :: unit logical :: exist ser%indentation = " " if (command_argument_count() > 0) then do iarg = 1, command_argument_count() if (allocated(argument)) deallocate(argument) call get_command_argument(iarg, length=length) allocate(character(len=length) :: argument) exist = length > 0 if (exist) then call get_command_argument(iarg, argument) inquire(file=argument, exist=exist) end if if (exist) then open(newunit=unit, file=argument) if (allocated(table)) deallocate(table) call toml_parse(table, unit, error) close(unit) if (allocated(error)) then write(error_unit, '(a)') error%message end if if (allocated(table)) then call table%accept(ser) call table%destroy end if end if end do else call toml_parse(table, input_unit, error) if (allocated(error)) then write(error_unit, '(a)') error%message end if if (allocated(table)) then call table%accept(ser) call table%destroy else error stop end if end if if (allocated(argument)) deallocate(argument) end program toml2json fortran-toml-0.5.0/test/compliance/json_lexer.f900000664000175000017500000003576615201541453022126 0ustar alastairalastair! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. module tjson_lexer use tomlf_constants, only : tfc, tfi, tfr, toml_escape use tomlf_datetime, only : toml_datetime use tomlf_de_abc, only : abstract_lexer use tomlf_de_token, only : toml_token, token_kind use tomlf_error, only : toml_error, make_error use tomlf_utils, only : read_whole_file, read_whole_line implicit none private public :: json_lexer public :: new_lexer_from_file, new_lexer_from_unit, new_lexer_from_string public :: toml_token, token_kind !> Tokenizer for JSON documents type, extends(abstract_lexer) :: json_lexer !> Name of the source file, used for error reporting character(len=:), allocatable :: filename !> Current internal position in the source chunk integer :: pos = 0 !> Current source chunk character(:, tfc), allocatable :: chunk !> Additional tokens to insert before the actual token stream integer :: prelude = 2 contains !> Obtain the next token procedure :: next !> Extract a string from a token procedure :: extract_string !> Extract an integer from a token procedure :: extract_integer !> Extract a float from a token procedure :: extract_float !> Extract a boolean from a token procedure :: extract_bool !> Extract a timestamp from a token procedure :: extract_datetime !> Get information about source procedure :: get_info end type json_lexer character(*, tfc), parameter :: terminated = " {}[],:"//& & toml_escape%tabulator//toml_escape%newline//toml_escape%carriage_return contains !> Create a new instance of a lexer by reading from a file subroutine new_lexer_from_file(lexer, filename, error) !> Instance of the lexer type(json_lexer), intent(out) :: lexer !> Name of the file to read from character(len=*), intent(in) :: filename !> Error code type(toml_error), allocatable, intent(out) :: error integer :: stat lexer%filename = filename call read_whole_file(filename, lexer%chunk, stat) if (stat /= 0) call make_error(error, "Could not open file '"//filename//"'") end subroutine new_lexer_from_file !> Create a new instance of a lexer by reading from a unit. !> !> Currently, only sequential access units can be processed by this constructor. subroutine new_lexer_from_unit(lexer, io, error) !> Instance of the lexer type(json_lexer), intent(out) :: lexer !> Unit to read from integer, intent(in) :: io !> Error code type(toml_error), allocatable, intent(out) :: error character(:, tfc), allocatable :: source, line integer, parameter :: bufsize = 512 character(bufsize, tfc) :: filename, mode integer :: stat inquire(unit=io, access=mode, name=filename) select case(trim(mode)) case default stat = 1 case("sequential", "SEQUENTIAL") allocate(character(0) :: source) do call read_whole_line(io, line, stat) if (stat > 0) exit source = source // line // toml_escape%newline if (stat < 0) then if (is_iostat_end(stat)) stat = 0 exit end if end do call new_lexer_from_string(lexer, source) end select if (len_trim(filename) > 0) lexer%filename = trim(filename) if (stat /= 0) call make_error(error, "Failed to read from unit") end subroutine new_lexer_from_unit !> Create a new instance of a lexer by reading from a string. subroutine new_lexer_from_string(lexer, string) !> Instance of the lexer type(json_lexer), intent(out) :: lexer !> String to read from character(len=*), intent(in) :: string lexer%chunk = string end subroutine new_lexer_from_string !> Extract information about the source subroutine get_info(lexer, meta, output) !> Instance of the lexer class(json_lexer), intent(in) :: lexer !> Query about the source character(*, tfc), intent(in) :: meta !> Metadata about the source character(:, tfc), allocatable, intent(out) :: output select case(meta) case("source") output = lexer%chunk // toml_escape%newline case("filename") if (allocated(lexer%filename)) output = lexer%filename end select end subroutine get_info !> Advance to the next token in the lexer subroutine next(lexer, token) !> Instance of the lexer class(json_lexer), intent(inout) :: lexer !> Current token type(toml_token), intent(inout) :: token type(toml_token), parameter :: prelude(2) = & [toml_token(token_kind%equal, 0, 0), toml_token(token_kind%keypath, 1, 0)] if (lexer%prelude > 0) then token = prelude(lexer%prelude) lexer%prelude = lexer%prelude - 1 return end if call next_token(lexer, token) end subroutine next !> Actually generate the next token, unbuffered version subroutine next_token(lexer, token) !> Instance of the lexer class(json_lexer), intent(inout) :: lexer !> Current token type(toml_token), intent(inout) :: token integer :: prev, pos ! Consume current token lexer%pos = lexer%pos + token%last - token%first + 1 prev = lexer%pos pos = lexer%pos ! If lexer is exhausted, return EOF as early as possible if (pos > len(lexer%chunk)) then token = toml_token(token_kind%eof, prev, pos) return end if select case(lexer%chunk(pos:pos)) case(" ", toml_escape%tabulator, toml_escape%newline, toml_escape%carriage_return) do while(any(lexer%chunk(pos+1:pos+1) == [" ", toml_escape%tabulator, & & toml_escape%newline, toml_escape%carriage_return]) .and. pos < len(lexer%chunk)) pos = pos + 1 end do token = toml_token(token_kind%whitespace, prev, pos) return case(":") token = toml_token(token_kind%equal, prev, pos) return case("{") token = toml_token(token_kind%lbrace, prev, pos) return case("}") token = toml_token(token_kind%rbrace, prev, pos) return case("[") token = toml_token(token_kind%lbracket, prev, pos) return case("]") token = toml_token(token_kind%rbracket, prev, pos) return case('"') call next_string(lexer, token) return case("-", "0":"9") call next_number(lexer, token) if (token%kind /= token_kind%invalid) return case("t", "f") call next_boolean(lexer, token) return case("n") call next_null(lexer, token) return case(",") token = toml_token(token_kind%comma, prev, pos) return end select ! If the current token is invalid, advance to the next terminator do while(verify(lexer%chunk(pos+1:pos+1), terminated) > 0 .and. pos < len(lexer%chunk)) pos = pos + 1 end do token = toml_token(token_kind%invalid, prev, pos) end subroutine next_token !> Process next string token subroutine next_string(lexer, token) !> Instance of the lexer type(json_lexer), intent(inout) :: lexer !> Current token type(toml_token), intent(inout) :: token character(1, tfc) :: ch character(*, tfc), parameter :: valid_escape = 'btnfr\"' integer :: prev, pos logical :: escape, valid prev = lexer%pos pos = lexer%pos valid = .true. escape = .false. do while(pos < len(lexer%chunk)) pos = pos + 1 ch = lexer%chunk(pos:pos) valid = valid .and. valid_string(ch) if (escape) then escape = .false. valid = valid .and. verify(ch, valid_escape) == 0 cycle end if escape = ch == toml_escape%backslash if (ch == '"') exit if (ch == toml_escape%newline) then pos = pos - 1 valid = .false. exit end if end do valid = valid .and. lexer%chunk(pos:pos) == '"' .and. pos /= prev token = toml_token(merge(token_kind%string, token_kind%invalid, valid), prev, pos) end subroutine next_string !> Process next number token, can produce either integer or floats subroutine next_number(lexer, token) !> Instance of the lexer type(json_lexer), intent(inout) :: lexer !> Current token type(toml_token), intent(inout) :: token integer :: prev, pos, point, expo logical :: minus, zero, first character(1, tfc) :: ch integer, parameter :: offset(*) = [0, 1, 2] prev = lexer%pos pos = lexer%pos token = toml_token(token_kind%invalid, prev, pos) point = 0 expo = 0 zero = .false. first = .true. minus = lexer%chunk(pos:pos) == "-" if (minus) pos = pos + 1 do while(pos <= len(lexer%chunk)) ch = lexer%chunk(pos:pos) if (ch == ".") then if (point > 0 .or. expo > 0) return zero = .false. point = pos pos = pos + 1 cycle end if if (ch == "e" .or. ch == "E") then if (expo > 0) return zero = .false. expo = pos pos = pos + 1 cycle end if if (ch == "+" .or. ch == "-") then if (.not.any(lexer%chunk(pos-1:pos-1) == ["e", "E"])) return pos = pos + 1 cycle end if if (verify(ch, "0123456789") == 0) then if (zero) return zero = first .and. ch == "0" first = .false. pos = pos + 1 cycle end if exit end do if (any([expo, point] == pos-1)) return token = toml_token(merge(token_kind%float, token_kind%int, any([expo, point] > 0)), & & prev, pos-1) end subroutine next_number !> Process next boolean token subroutine next_boolean(lexer, token) !> Instance of the lexer type(json_lexer), intent(inout) :: lexer !> Current token type(toml_token), intent(inout) :: token integer :: pos, prev prev = lexer%pos pos = lexer%pos do while(verify(lexer%chunk(pos+1:pos+1), terminated) > 0 .and. pos < len(lexer%chunk)) pos = pos + 1 end do select case(lexer%chunk(prev:pos)) case default token = toml_token(token_kind%invalid, prev, pos) case("true", "false") token = toml_token(token_kind%bool, prev, pos) end select end subroutine next_boolean !> Process next null token subroutine next_null(lexer, token) !> Instance of the lexer type(json_lexer), intent(inout) :: lexer !> Current token type(toml_token), intent(inout) :: token integer :: pos, prev prev = lexer%pos pos = lexer%pos do while(verify(lexer%chunk(pos+1:pos+1), terminated) > 0 .and. pos < len(lexer%chunk)) pos = pos + 1 end do token = toml_token( & & merge(token_kind%nil, token_kind%invalid, lexer%chunk(prev:pos) == "null"), & & prev, pos) end subroutine next_null !> Validate characters in string, non-printable characters are invalid in this context pure function valid_string(ch) result(valid) character(1, tfc), intent(in) :: ch logical :: valid character(1, tfc), parameter :: x00 = achar(int(z"00")), x08 = achar(int(z"08")), & & x0b = achar(int(z"0b")), x1f = achar(int(z"1f")), x7f = achar(int(z"7f")) valid = & & .not.(x00 <= ch .and. ch <= x08) .and. & & .not.(x0b <= ch .and. ch <= x1f) .and. & & ch /= x7f end function valid_string !> Extract string value of token subroutine extract_string(lexer, token, string) !> Instance of the lexer class(json_lexer), intent(in) :: lexer !> Token to extract string value from type(toml_token), intent(in) :: token !> String value of token character(len=:), allocatable, intent(out) :: string integer :: it, length logical :: escape character(1, tfc) :: ch length = token%last - token%first + 1 select case(token%kind) case(token_kind%keypath) ! dummy token inserted by lexer prelude string = "_" case(token_kind%string) string = "" escape = .false. do it = token%first + 1, token%last - 1 ch = lexer%chunk(it:it) if (escape) then escape = .false. select case(ch) case(toml_escape%dquote, toml_escape%backslash); string = string // ch case("b"); string = string // toml_escape%bspace case("t"); string = string // toml_escape%tabulator case("n"); string = string // toml_escape%newline case("r"); string = string // toml_escape%carriage_return case("f"); string = string // toml_escape%formfeed end select cycle end if escape = ch == toml_escape%backslash if (.not.escape) string = string // ch end do end select end subroutine extract_string !> Extract integer value of token subroutine extract_integer(lexer, token, val) !> Instance of the lexer class(json_lexer), intent(in) :: lexer !> Token to extract integer value from type(toml_token), intent(in) :: token !> Integer value of token integer(tfi), intent(out) :: val integer :: first, it, tmp character(1, tfc) :: ch character(*, tfc), parameter :: num = "0123456789" if (token%kind /= token_kind%int) return val = 0 first = token%first if (lexer%chunk(first:first) == "-") first = first + 1 if (lexer%chunk(first:first) == "0") return do it = first, token%last ch = lexer%chunk(it:it) tmp = scan(num, ch) - 1 if (tmp < 0) cycle val = val * 10 - tmp end do if (lexer%chunk(token%first:token%first) /= "-") val = -val end subroutine extract_integer !> Extract floating point value of token subroutine extract_float(lexer, token, val) use, intrinsic :: ieee_arithmetic, only : ieee_value, & & ieee_positive_inf, ieee_negative_inf, ieee_quiet_nan !> Instance of the lexer class(json_lexer), intent(in) :: lexer !> Token to extract floating point value from type(toml_token), intent(in) :: token !> Floating point value of token real(tfr), intent(out) :: val integer :: stat if (token%kind /= token_kind%float) return read(lexer%chunk(token%first:token%last), *, iostat=stat) val end subroutine extract_float !> Extract boolean value of token subroutine extract_bool(lexer, token, val) !> Instance of the lexer class(json_lexer), intent(in) :: lexer !> Token to extract boolean value from type(toml_token), intent(in) :: token !> Boolean value of token logical, intent(out) :: val if (token%kind /= token_kind%bool) return val = lexer%chunk(token%first:token%last) == "true" end subroutine extract_bool !> Extract datetime value of token subroutine extract_datetime(lexer, token, val) !> Instance of the lexer class(json_lexer), intent(in) :: lexer !> Token to extract datetime value from type(toml_token), intent(in) :: token !> Datetime value of token type(toml_datetime), intent(out) :: val associate(lexer => lexer, token => token) ! ignore unused dummy arguments end associate end subroutine extract_datetime end module tjson_lexer fortran-toml-0.5.0/test/compliance/CMakeLists.txt0000664000175000017500000000174415201541453022163 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Serializer from TOML to a specific JSON format suitable for the validator suite add_executable( toml2json "json_ser.f90" "toml2json.f90" ) target_link_libraries( toml2json PRIVATE "${PROJECT_NAME}-lib" ) # Deserializer for a specific JSON format suitable for the validator suite add_executable( json2toml "json_lexer.f90" "json_parser.f90" "json2toml.f90" ) target_link_libraries( json2toml PRIVATE "${PROJECT_NAME}-lib" ) fortran-toml-0.5.0/README.md0000664000175000017500000003044215201541453015606 0ustar alastairalastair# TOML parser for Fortran projects [![License](https://img.shields.io/badge/license-MIT%7CApache%202.0-blue)](LICENSE-Apache) [![Release](https://img.shields.io/github/v/release/toml-f/toml-f)](https://github.com/toml-f/toml-f/releases/latest) [![Build](https://github.com/toml-f/toml-f/workflows/CI/badge.svg)](https://github.com/toml-f/toml-f/actions) [![docs](https://github.com/toml-f/toml-f/workflows/docs/badge.svg)](https://toml-f.github.io/toml-f) [![Documentation Status](https://readthedocs.org/projects/toml-f/badge/?version=latest)](https://toml-f.readthedocs.io) [![codecov](https://codecov.io/gh/toml-f/toml-f/branch/main/graph/badge.svg)](https://codecov.io/gh/toml-f/toml-f) A TOML parser implementation for data serialization and deserialization in Fortran. * the [TOML standard](https://toml.io) * currently supported [TOML v1.0.0 specification](https://toml.io/en/v1.0.0) * [TOML Fortran documentation](https://toml-f.readthedocs.io)
TOML-Fortran
## Installation The TOML Fortran library is available via various distribution channels. If your channel is not found here, please checkout the instructions for [building from source](#building-from-source). You can also find these instructions in the user documentation at [Installing TOML Fortran](https://toml-f.readthedocs.io/en/latest/how-to/installation.html). ### Conda package [![Conda](https://img.shields.io/conda/vn/conda-forge/toml-f)](https://github.com/conda-forge/toml-f-feedstock) [![Conda](https://img.shields.io/conda/pn/conda-forge/toml-f)](https://github.com/conda-forge/toml-f-feedstock) This project is packaged for the *mamba* package manager and available on the *conda-forge* channel. To install the *mamba* package manager we recommend the [miniforge](https://github.com/conda-forge/miniforge/releases) installer. If the *conda-forge* channel is not yet enabled, add it to your channels with ``` mamba config --add channels conda-forge mamba config --set channel_priority strict ``` Once the *conda-forge* channel has been enabled, TOML Fortran can be installed with *mamba*: ``` mamba install toml-f ``` It is possible to list all of the versions of TOML Fortran available on your platform with *mamba*: ``` mamba repoquery search toml-f --channel conda-forge ``` ### FreeBSD port [![FreeBSD port](https://repology.org/badge/version-for-repo/freebsd/toml-f.svg)](https://www.freshports.org/textproc/toml-f/) A port for FreeBSD is available and can be installed using ``` pkg install textproc/toml-f ``` In case no package is available build the port using ``` cd /usr/ports/textproc/toml-f make install clean ``` For more information see the [toml-f port details](https://www.freshports.org/textproc/toml-f/). ### Alternative distributions Please let us know if you are packaging TOML Fortran. Other available distributions of TOML Fortran currently include - Homebrew tap at [grimme-lab/homebrew-qc](https://github.com/grimme-lab/homebrew-qc) - Easy-build config at [easybuilders/easybuild-easyconfigs](https://github.com/easybuilders/easybuild-easyconfigs/tree/develop/easybuild/easyconfigs/t/TOML-Fortran) - spack package in [develop](https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/toml-f/package.py) An overview of the availability of TOML Fortran in distributions tracked by [Repology](https://repology.org) is provided here: [![Packaging status](https://repology.org/badge/vertical-allrepos/toml-f.svg)](https://repology.org/project/toml-f/versions) ### Building from source To build this project from the source code in this repository you need to have - a Fortran compiler supporting Fortran 2008 - GFortran 5 or newer - Intel Fortran 18 or newer - NAG 7 or newer - One of the supported build systems - [meson](https://mesonbuild.com) version 0.55 or newer - [CMake](https://cmake.org/) version 3.9 or newer - [Fortran package manager (fpm)](https://github.com/fortran-lang/fpm) version 0.2.0 or newer Get the source by cloning the repository ``` git clone https://github.com/toml-f/toml-f cd toml-f ``` #### Building with meson To integrate TOML Fortran in your meson project checkout the [Integrate with meson](https://toml-f.readthedocs.io/en/latest/how-to/integration.html#integrate-with-meson) recipe. To build this project with meson a build-system backend is required, *i.e.* [ninja](https://ninja-build.org) version 1.7 or newer. Setup a build with ``` meson setup _build --prefix=/path/to/install ``` You can select the Fortran compiler by the `FC` environment variable. To compile the project run ``` meson compile -C _build ``` We employ a [validator suite](https://github.com/BurntSushi/toml-test) to test the standard compliance of this implementation. To use this testing a `go` installation is required. The installation of the validator suite will be handled by meson automatically without installing into the users `go` workspace. Run the tests with ``` meson test -C _build --print-errorlogs ``` The binary used for transcribing the TOML documents to the testing format is `_build/test/toml2json` and can be used to check on per test basis. Finally, you can install TOML Fortran using ``` meson install -C _build ``` ### Building with CMake To integrate TOML Fortran in your CMake project checkout the [Integrate with CMake](https://toml-f.readthedocs.io/en/latest/how-to/integration.html#integrate-with-cmake) recipe. While meson is the preferred way to build this project it also offers CMake support. Configure the CMake build with ``` cmake -B _build -G Ninja -DCMAKE_INSTALL_PREFIX=/path/to/install ``` Similar to meson the compiler can be selected with the `FC` environment variable. You can build the project using ``` cmake --build _build ``` You can run basic unit tests using ``` ctest --test-dir _build ``` The validation suite is currently not supported as unit test for CMake builds and requires a manual setup instead using the `toml2json` binary. Finally, you can install TOML Fortran using ``` cmake --install _build ``` ### Building with fpm To integrate TOML Fortran in your fpm project checkout the [Using the Fortran package manager](https://toml-f.readthedocs.io/en/latest/how-to/integration.html#using-the-fortran-package-manager) recipe. The Fortran package manager ([fpm](https://github.com/fortran-lang/fpm)) supports the addition of TOML Fortran as a dependency. In the package manifest, `fpm.toml`, you can add TOML Fortran dependency via: ```toml [dependencies] toml-f.git = "https://github.com/toml-f/toml-f" ``` Then build and test normally. ``` fpm build fpm test ``` A more detailed example is described in [example 1](test/example-1). ## Documentation The user documentation is available at [readthedocs](https://toml-f.readthedocs.io). Additionally, the [FORD](https://github.com/Fortran-FOSS-Programmers/ford) generated API documentation is available [here](https://toml-f.github.io/toml-f). To build the user documentation locally we use sphinx, install the dependencies you can use the *mamba* package manager ``` mamba create -n sphinx --file doc/requirements.txt mamba activate sphinx ``` The documentation is build with ``` sphinx-build doc _doc ``` You can inspect the generated documentation by starting a webserver ``` python3 -m http.server -d _doc ``` And open the shown URL in a browser. ### Translating the documentation The documentation of TOML Fortran can be fully translated. Before adding a translation, reach out to the repository maintainers by creating and issue or starting a discussion thread. To start a new translation you need the `sphinx-intl` package which can be installed with *mamba* ``` mamba install -n sphinx sphinx-intl ``` To add a new language to the translation extract the text with `sphinx-build` and create the respective locales with `sphinx-intl` using the commands shown below. ``` sphinx-build -b gettext doc _gettext sphinx-intl update -l en -p _gettext -d doc/locales ``` Replace the argument to the language flag `-l` with your target language, the language keys are listed [here](https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language). The same workflow can be used for updating existing locales. The translation files are available in `doc/locales` and can be translated using a translation-editor, like [gtranslator](https://wiki.gnome.org/Apps/Gtranslator) or [poedit](https://poedit.net/). After a new translation is merged, a maintainer will create a new translation for the readthedocs to ensure it shows up at the pages. ### Generating the API docs The API documentation is generated with [FORD](https://github.com/Fortran-FOSS-Programmers/ford). We are looking for a better tool to automatically extract the documentation, suggestions and help with this effort are welcome. The required programs can be installed with *mamba* ``` mamba create -n ford ford mamba activate ford ``` To generate the pages use ``` ford docs.md -o _ford ``` You can inspect the generated documentation by starting a webserver ``` python3 -m http.server -d _ford ``` ## Usage To make use of this library use the `tomlf` module in your projects. You can access the individual modules but those are not considered part of the public API and might change between versions. An example program to load and dump a TOML file would look like this: ```fortran use tomlf implicit none character(len=*), parameter :: nl = new_line("a") type(toml_table), allocatable :: table character(len=:), allocatable :: input_string type(toml_serializer) :: ser input_string = & & '# This is a TOML document.' // nl // & & 'title = "TOML Example"' // nl // & & '[owner]' // nl // & & 'name = "Tom Preston-Werner"' // nl // & & 'dob = 1979-05-27T07:32:00-08:00 # First class dates' // nl // & & '[database]' // nl // & & 'server = "192.168.1.1"' // nl // & & 'ports = [ 8001, 8001, 8002 ]' // nl // & & 'connection_max = 5000' // nl // & & 'enabled = true' // nl // & & '[servers]' // nl // & & ' # Indentation (tabs and/or spaces) is allowed but not required' // nl // & & ' [servers.alpha]' // nl // & & ' ip = "10.0.0.1"' // nl // & & ' dc = "eqdc10"' // nl // & & ' [servers.beta]' // nl // & & ' ip = "10.0.0.2"' // nl // & & ' dc = "eqdc10"' // nl // & & '[clients]' // nl // & & 'data = [ ["gamma", "delta"], [1, 2] ]' // nl // & & '# Line breaks are OK when inside arrays' // nl // & & 'hosts = [' // nl // & & ' "alpha",' // nl // & & ' "omega"' // nl // & & ']' call toml_loads(table, input_string) if (allocated(table)) then call table%accept(ser) call table%destroy ! not necessary end if end ``` Here the TOML document is provided as string, notice that you have to add a newline character by using the intrinsic function `new_line("a")` to get the lines correctly. Alternatively, a file can be loaded from any connected, formatted unit using the same overloaded function. For the standard input the intrinsic `input_unit` should be passed. If the TOML file is successfully parsed the table will be allocated and can be written to the standard output by passing the `toml_serializer` as visitor to the table. For more details checkout the [documentation pages](https://toml-f.readthedocs.io). If you find an error in the documentation or a part is incomplete, please [open an issue](https://github.com/toml-f/toml-f/issues) or start a [discussion thread](https://github.com/orgs/toml-f/discussions). ## Contributing This is a volunteer open source project and contributions are always welcome. Please, take a moment to read the [contributing guidelines](CONTRIBUTING.md) on how to get involved in TOML-Fortran. ## License TOML-Fortran is free software: you can redistribute it and/or modify it under the terms of the [Apache License, Version 2.0](LICENSE-Apache) or [MIT license](LICENSE-MIT) at your opinion. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an _as is_ basis, without warranties or conditions of any kind, either express or implied. See the License for the specific language governing permissions and limitations under the License. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in TOML-Fortran by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. fortran-toml-0.5.0/config/0000775000175000017500000000000015201541453015571 5ustar alastairalastairfortran-toml-0.5.0/config/template.cmake0000664000175000017500000000021415201541453020403 0ustar alastairalastair@PACKAGE_INIT@ if(NOT TARGET "@PROJECT_NAME@::@PROJECT_NAME@") include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") endif() fortran-toml-0.5.0/config/meson.build0000664000175000017500000000234515201541453017737 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. os = host_machine.system() if os == 'windows' add_project_link_arguments( '-Wl,--allow-multiple-definition', language: 'fortran', ) endif fc = meson.get_compiler('fortran') fc_id = fc.get_id() if fc_id == 'gcc' add_project_arguments( '-ffree-line-length-none', '-fbacktrace', language: 'fortran', ) elif fc_id == 'intel' add_project_arguments( '-traceback', language: 'fortran', ) elif fc_id == 'pgi' or fc_id == 'nvidia_hpc' add_project_arguments( '-Mbackslash', '-Mallocatable=03', '-traceback', language: 'fortran', ) elif fc_id == 'flang' add_project_arguments( '-Mbackslash', '-Mallocatable=03', language: 'fortran', ) endif fortran-toml-0.5.0/config/install-mod.py0000664000175000017500000000247315201541453020374 0ustar alastairalastair#!/usr/bin/env python3 # This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from os import environ, listdir, makedirs from os.path import join, isdir, exists from sys import argv from shutil import copy build_dir = environ["MESON_BUILD_ROOT"] if "MESON_INSTALL_DESTDIR_PREFIX" in environ: install_dir = environ["MESON_INSTALL_DESTDIR_PREFIX"] else: install_dir = environ["MESON_INSTALL_PREFIX"] include_dir = argv[1] if len(argv) > 1 else "include" module_dir = join(install_dir, include_dir) modules = [] for d in listdir(build_dir): bd = join(build_dir, d) if isdir(bd): for f in listdir(bd): if f.endswith(".mod"): modules.append(join(bd, f)) if not exists(module_dir): makedirs(module_dir) for mod in modules: print("Installing", mod, "to", module_dir) copy(mod, module_dir) fortran-toml-0.5.0/config/cmake/0000775000175000017500000000000015201541453016651 5ustar alastairalastairfortran-toml-0.5.0/config/cmake/Findtest-drive.cmake0000664000175000017500000000677215201541453022556 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set(_lib "test-drive") set(_pkg "TEST_DRIVE") set(_url "https://github.com/fortran-lang/test-drive") set(_rev "v0.4.0") if(NOT DEFINED "${_pkg}_FIND_METHOD") if(DEFINED "${PROJECT_NAME}-dependency-method") set("${_pkg}_FIND_METHOD" "${${PROJECT_NAME}-dependency-method}") else() set("${_pkg}_FIND_METHOD" "cmake" "pkgconf" "subproject" "fetch") endif() set("_${_pkg}_FIND_METHOD") endif() foreach(method in ITEMS ${${_pkg}_FIND_METHOD}) if(TARGET "${_lib}::${_lib}") break() endif() if("${method}" STREQUAL "cmake") message(STATUS "${_lib}: Find installed package") find_package("${_lib}" CONFIG) if("${_lib}_FOUND") message(STATUS "${_lib}: Found installed package") break() endif() endif() if("${method}" STREQUAL "pkgconf") find_package(PkgConfig QUIET) pkg_check_modules("${_pkg}" QUIET "${_lib}") if("${_pkg}_FOUND") message(STATUS "Found ${_lib} via pkg-config") add_library("${_lib}::${_lib}" INTERFACE IMPORTED) target_link_libraries( "${_lib}::${_lib}" INTERFACE "${${_pkg}_LINK_LIBRARIES}" ) target_include_directories( "${_lib}::${_lib}" INTERFACE "${${_pkg}_INCLUDE_DIRS}" ) break() endif() endif() if("${method}" STREQUAL "subproject") set("${_pkg}_SOURCE_DIR" "${PROJECT_SOURCE_DIR}/subprojects/${_lib}") set("${_pkg}_BINARY_DIR" "${PROJECT_BINARY_DIR}/subprojects/${_lib}") if(EXISTS "${${_pkg}_SOURCE_DIR}/CMakeLists.txt") message(STATUS "Include ${_lib} from subprojects") add_subdirectory( "${${_pkg}_SOURCE_DIR}" "${${_pkg}_BINARY_DIR}" ) add_library("${_lib}::${_lib}" INTERFACE IMPORTED) target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") # We need the module directory in the subproject before we finish the configure stage if(NOT EXISTS "${${_pkg}_BINARY_DIR}/include") file(MAKE_DIRECTORY "${${_pkg}_BINARY_DIR}/include") endif() break() endif() endif() if("${method}" STREQUAL "fetch") message(STATUS "Retrieving ${_lib} from ${_url}") include(FetchContent) FetchContent_Declare( "${_lib}" GIT_REPOSITORY "${_url}" GIT_TAG "${_rev}" ) FetchContent_MakeAvailable("${_lib}") add_library("${_lib}::${_lib}" INTERFACE IMPORTED) target_link_libraries("${_lib}::${_lib}" INTERFACE "${_lib}") # We need the module directory in the subproject before we finish the configure stage FetchContent_GetProperties("${_lib}" BINARY_DIR "${_pkg}_BINARY_DIR") if(NOT EXISTS "${${_pkg}_BINARY_DIR}/include") file(MAKE_DIRECTORY "${${_pkg}_BINARY_DIR}/include") endif() break() endif() endforeach() if(NOT TARGET "${_lib}::${_lib}") message(FATAL_ERROR "Could not find dependency ${_lib}") endif() if(DEFINED "_${_pkg}_FIND_METHOD") unset("${_pkg}_FIND_METHOD") unset("_${_pkg}_FIND_METHOD") endif() unset(_lib) unset(_pkg) unset(_url) unset(_rev) fortran-toml-0.5.0/config/CMakeLists.txt0000664000175000017500000000417615201541453020341 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. option(BUILD_SHARED_LIBS "Whether the libraries built should be shared" FALSE) set( "${PROJECT_NAME}-module-dir" "${PROJECT_NAME}/modules" CACHE STRING "Subdirectory to install generated module files to" ) set( module-dir "${${PROJECT_NAME}-module-dir}" ) set(module-dir "${module-dir}" PARENT_SCOPE) # Set build type as CMake does not provide defaults if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set( CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build type to be used." FORCE ) message( STATUS "Setting build type to '${CMAKE_BUILD_TYPE}' as none was specified." ) endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) include(CMakePackageConfigHelpers) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/template.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" VERSION "${PROJECT_VERSION}" COMPATIBILITY SameMinorVersion ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/template.pc" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" @ONLY ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" ) fortran-toml-0.5.0/config/template.pc0000664000175000017500000000044215201541453017730 0ustar alastairalastairprefix=@CMAKE_INSTALL_PREFIX@ libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: @PROJECT_NAME@ Description: @PROJECT_DESCRIPTION@ Version: @PROJECT_VERSION@ Libs: -L${libdir} -l@PROJECT_NAME@ Cflags: -I${includedir} -I${includedir}/@module-dir@ fortran-toml-0.5.0/.codecov.yml0000664000175000017500000000010615201541453016544 0ustar alastairalastairfixes: - "/home/runner/work/toml-f/toml-f::" ignore: - "test/**" fortran-toml-0.5.0/CONTRIBUTING.md0000664000175000017500000001110315201541453016551 0ustar alastairalastair# Contributing to TOML-Fortran First off, thank you for considering contributing to TOML-Fortran. Please take a moment to review this guidelines to make the contribution process simple and effective for all involved. Respecting these guidelines helps communicate that you respect the time of the developers who manage and develop this open source project. In return, they should return this respect by addressing your problem, evaluating changes, and helping you handle your pull requests. ## Reporting a Bug A bug is a _demonstratable problem_ caused by the code in this repository. Good bug reports are extremely valuable for us - thank you! Before opening a bug report: 1. Check if the issue has already been reported. 2. Check if it still is an issue or has already been fixed? Try to reproduce it with the latest version from the default branch. 3. Isolate the problem and create a reduced test case. A good bug report should not leave others needing to chase you up for more information. So please try to be as detailed as possible in your report, answer at least these questions: 1. Which version of TOML-Fortran are you using? The current version is always a subject to change, so be more specific. 2. What steps will reproduce the issue? We have to reproduce the issue, so we need all the input files. 3. What would be the expected outcome? 4. What did you see instead? All these details will help people to fix any potential bugs. ## Suggesting a New Feature Feature requests are welcome. But take a moment to find out if your idea fits the scope and goals of the project. It is up to you to provide a strong argument to convince the project's developers of the benefits of this feature. Please provide as much detail and context as possible. ## Implementing a New Feature Contributions are welcome via GitHub pull requests. - Each pull request should implement _one_ feature or fix _one_ bug. If you want to add or fix more than one thing, submit more than one pull request. - Do not commit changes to files that are irrelevant to your feature or bugfix (_e.g._ `.gitignore`). - Be willing to accept criticism and work on improving your code. ### For New Contributors If you never created a pull request before, welcome :tada:. You can learn how from [this great tutorial](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). Don't know where to start? You can start by looking through these [help-wanted issues](https://github.com/toml-f/toml-f/issues?q=label%3A%22help+wanted%22+is%3Aissue+is%3Aopen). ## Sign Your Work The sign-off is a simple line at the end of the explanation for a commit. All commits needs to be signed. Your signature certifies that you wrote the patch or otherwise have the right to contribute the material. The rules are pretty simple, if you can certify the below (from [developercertificate.org](https://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 1 Letterman Drive Suite D4700 San Francisco, CA, 94129 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` Then you just add a line to every git commit message: ``` Signed-off-by: Joe Smith ``` Use your real name (sorry, no pseudonyms or anonymous contributions.) If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. fortran-toml-0.5.0/docs.md0000664000175000017500000000350515201541453015601 0ustar alastairalastairWelcome to the API documentation for **TOML Fortran** (toml-f), a TOML parser implementation for data serialization and deserialization in Fortran. ## Getting Started To use TOML Fortran in your project, import the main module: ```fortran use tomlf ``` This provides access to all public types and procedures for parsing, manipulating, and serializing TOML data. ## Core Functionality ### Parsing TOML - [[toml_load]] - Load a TOML document from a file - [[toml_loads]] - Parse a TOML document from a string - [[toml_parse]] - Parse TOML from a connected unit ### Serialization - [[toml_dump]] - Write a TOML table to a file - [[toml_dumps]] - Serialize a TOML table to a string - [[toml_serialize]] - Serialize using the visitor pattern ### Data Access - [[get_value]] - Retrieve values from TOML tables and arrays - [[set_value]] - Set values in TOML tables and arrays ## Data Types | Type | Description | |------|-------------| | [[toml_table]] | TOML table (key-value mapping) | | [[toml_array]] | TOML array | | [[toml_keyval]] | Single key-value pair | | [[toml_value]] | Abstract base type for all TOML values | | [[toml_datetime]] | TOML datetime representation | | [[toml_error]] | Error information from parsing | ## Example ```fortran use tomlf implicit none type(toml_table), allocatable :: table type(toml_error), allocatable :: error character(len=:), allocatable :: title call toml_load(table, "config.toml", error=error) if (allocated(error)) then print '(a)', error%message stop 1 end if call get_value(table, "title", title) print '(a)', title ``` ## Further Resources - [User Documentation](https://toml-f.readthedocs.io) - Tutorials and how-to guides - [TOML Specification](https://toml.io/en/v1.0.0) - The TOML v1.0.0 standard - [GitHub Repository](https://github.com/toml-f/toml-f) - Source code and issue tracker fortran-toml-0.5.0/AGENTS.md0000664000175000017500000001625415201541453015637 0ustar alastairalastair# AGENTS.md - Guidelines for AI Coding Assistants This document provides guidance for AI coding assistants (GitHub Copilot, Claude, etc.) working with the TOML-Fortran (toml-f) codebase. ## Project Overview TOML-Fortran is a TOML parser implementation for data serialization and deserialization in Fortran. It provides a complete implementation of the [TOML v1.0.0 specification](https://toml.io/en/v1.0.0). - **Language**: Fortran 2008 - **License**: Apache-2.0 OR MIT (dual-licensed) - **Repository**: https://github.com/toml-f/toml-f ## Code Structure ``` toml-f/ ├── src/ # Main source code │ ├── tomlf.f90 # Public API module │ └── tomlf/ # Implementation modules │ ├── build/ # Builder utilities (get_value, set_value) │ ├── de/ # Deserializer (parser, lexer) │ ├── ser.f90 # Serializer │ ├── structure/ # Internal generic data structures (list, map, ordered_map, etc.) │ ├── type/ # Type definitions (TOML types: toml_table, toml_array, toml_keyval) │ ├── utils/ # Utility functions │ └── ... ├── test/ # Test suite │ ├── unit/ # Unit tests (using test-drive framework) │ ├── compliance/ # TOML compliance tests (toml2json, json2toml) │ ├── example-1/ # Example programs │ └── version/ # Version tests ├── doc/ # Sphinx documentation source ├── config/ # Build configuration files └── subprojects/ # Meson subprojects (test-drive) ``` ## Build Systems This project supports three build systems: ### Meson (Preferred) ```bash meson setup _build meson compile -C _build meson test -C _build --print-errorlogs ``` ### CMake ```bash cmake -B _build -G Ninja cmake --build _build ctest --test-dir _build ``` ### Fortran Package Manager (fpm) ```bash fpm build fpm test ``` ## Coding Conventions ### File Headers All source files must include the SPDX license header: ```fortran ! This file is part of toml-f. ! SPDX-Identifier: Apache-2.0 OR MIT ! ! Licensed under either of Apache License, Version 2.0 or MIT license ! at your option; you may not use this file except in compliance with ! the License. ! ! Unless required by applicable law or agreed to in writing, software ! distributed under the License is distributed on an "AS IS" BASIS, ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ! See the License for the specific language governing permissions and ! limitations under the License. ``` ### Fortran Style - Use **free-form** Fortran source format (`.f90` extension) - Use `implicit none` in all program units - Prefer `intent(in)`, `intent(out)`, or `intent(inout)` for all dummy arguments - Use descriptive variable and procedure names - Document public procedures with comment blocks describing parameters - Use `allocatable` for dynamic data rather than pointers where possible ### Naming Conventions - Module names: `tomlf_` (e.g., `tomlf_de`, `tomlf_ser`) - Public API uses `toml_` prefix (e.g., `toml_load`, `toml_dumps`, `toml_table`) - Type names: `toml_` (e.g., `toml_table`, `toml_array`) - Internal/private procedures use descriptive names without prefix - Test modules: `test_` (e.g., `test_lexer`) ### Module Structure ```fortran module tomlf_example use dependency_module, only : specific_imports implicit none private public :: public_type, public_procedure ! Type definitions ! Interface definitions contains ! Procedure implementations end module tomlf_example ``` ### Documentation - Use FORD-compatible docstrings (marked with `!>`) - User documentation is written in reStructuredText (Sphinx) ## Public API The main public API is exposed through the `tomlf` module (`src/tomlf.f90`): - **Parsing**: `toml_parse`, `toml_load`, `toml_loads` - **Serialization**: `toml_serialize`, `toml_dump`, `toml_dumps` - **Data access**: `get_value`, `set_value` - **Types**: `toml_table`, `toml_array`, `toml_keyval`, `toml_value` - **Utilities**: `toml_datetime`, `toml_error`, `toml_stat` Internal modules under `src/tomlf/` are not part of the public API and may change between versions. ## Testing Unit tests are located in `test/unit/`, using the [test-drive](https://github.com/fortran-lang/test-drive) framework. - Each test module should have a `collect_` subroutine that returns a testsuite - Run tests with: `meson test -C _build` or `fpm test` or `ctest` Key test files: - `test/unit/lexer.f90` - Lexer tests - `test/unit/parser.f90` - Parser tests - `test/unit/build.f90` - Builder tests - `test/unit/utils.f90` - Utility tests - `test/unit/sort.f90` - Sorting tests ### Adding New Tests 1. Create a new test file `test/unit/test_.f90` 2. Add the test to the build system files (`meson.build`, `CMakeLists.txt`) 3. Register the test collection in `test/unit/main.f90` ### Compliance Tests The project uses the [BurntSushi/toml-test](https://github.com/BurntSushi/toml-test) validator suite for TOML standard compliance testing. The `toml2json` and `json2toml` binaries in `test/compliance/` are used for this. ## Key Components ### Lexer (`src/tomlf/de/lexer.f90`) Tokenizes TOML input into a stream of tokens. ### Parser (`src/tomlf/de/parser.f90`) Parses token stream into TOML data structures. ### TOML Types (`src/tomlf/type/`) - `toml_table` - TOML table (hash map) - `toml_array` - TOML array - `toml_keyval` - Key-value pair ### Serializer (`src/tomlf/ser.f90`) Converts TOML data structures back to TOML format. ## Contributing Guidelines 1. **One feature/fix per PR** - Keep pull requests focused 2. **Sign commits** - Use `git commit -s` (Developer Certificate of Origin) 3. **Test changes** - Add or update tests for new functionality 4. **Follow style** - Match existing code formatting and conventions ## Dependencies - **Build-time**: Fortran compiler (GFortran 5+, Intel Fortran 18+, NAG 7+) - **test-drive** (v0.4.0): Unit testing framework (test dependency only) - **Compliance testing**: Go (for toml-test validator) ## When Making Changes 1. Ensure changes compile with all three build systems 2. Add or update unit tests for new functionality 3. Maintain backward compatibility with the public API 4. Update version numbers in `src/tomlf/version.f90` and build files for releases 5. Follow the existing code style in neighboring code ## Common Tasks ### Adding a new feature 1. Implement in appropriate module under `src/tomlf/` 2. Export through `src/tomlf.f90` if part of public API 3. Add unit tests in `test/unit/` 4. Update documentation in `doc/` ### Fixing a parser bug 1. Check `src/tomlf/de/lexer.f90` for tokenization issues 2. Check `src/tomlf/de/parser.f90` for parsing logic 3. Run compliance tests to verify fix ### Updating documentation - User docs: Edit files in `doc/` (reStructuredText) - API docs: Update docstrings in source files (FORD format) ## Resources - [TOML v1.0.0 Specification](https://toml.io/en/v1.0.0) - [TOML Fortran Documentation](https://toml-f.readthedocs.io) - [Fortran Best Practices](https://fortran-lang.org/learn/best_practices)fortran-toml-0.5.0/fpm.toml0000664000175000017500000000300415201541453016000 0ustar alastairalastairname = "toml-f" version = "0.5.0" license = "Apache-2.0 OR MIT" maintainer = ["@awvwgk"] author = ["Sebastian Ehlert"] copyright = "2019-2026 Sebastian Ehlert" homepage = "https://toml-f.readthedocs.org" keywords = ["toml", "io", "serde"] description = "TOML parser implementation for data serialization and deserialization" [library] source-dir = "src" [build] auto-tests = false [[test]] name = "tftest" source-dir = "test/unit" [test.dependencies] test-drive.git = "https://github.com/fortran-lang/test-drive.git" [[executable]] name = "toml2json" source-dir = "test/compliance" main = "toml2json.f90" [[executable]] name = "json2toml" source-dir = "test/compliance" main = "json2toml.f90" # FORD documentation settings [extra.ford] project = "TOML-Fortran" summary = "TOML parser implementation for data serialization and deserialization in Fortran." favicon = "./assets/toml-f-64x64.png" project_github = "https://github.com/toml-f/toml-f" project_download = "https://github.com/toml-f/toml-f/releases" project_website = "https://toml-f.readthedocs.io" author = "Sebastian Ehlert" github = "https://github.com/awvwgk" src_dir = ["./src"] exclude_dir = ["./test"] media_dir = "./assets" docmark = "<" predocmark = ">" source = true graph = true sort = "alpha" print_creation_date = true creation_date = "%Y-%m-%d %H:%M %z" md_extensions = ["markdown.extensions.toc", "markdown.extensions.smarty"] externalize = true [extra.ford.extra_mods] iso_fortran_env = "https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html" fortran-toml-0.5.0/LICENSE-Apache0000664000175000017500000002613615201541453016520 0ustar alastairalastair Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. fortran-toml-0.5.0/.gitignore0000664000175000017500000000045315201541453016316 0ustar alastairalastair# Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # Directories /build*/ /_*/ fortran-toml-0.5.0/meson_options.txt0000664000175000017500000000120015201541453017752 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. option('tests', type: 'boolean', value: true, description: 'Enable building and running tests') fortran-toml-0.5.0/.readthedocs.yaml0000664000175000017500000000107115201541453017552 0ustar alastairalastair# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: doc/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: doc/requirements.txt fortran-toml-0.5.0/CMakeLists.txt0000664000175000017500000000511215201541453017063 0ustar alastairalastair# This file is part of toml-f. # SPDX-Identifier: Apache-2.0 OR MIT # # Licensed under either of Apache License, Version 2.0 or MIT license # at your option; you may not use this file except in compliance with # the License. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. cmake_minimum_required(VERSION 3.9) get_directory_property(is-subproject PARENT_DIRECTORY) project( "toml-f" LANGUAGES "Fortran" VERSION "0.5.0" DESCRIPTION "A TOML parser implementation for data serialization and deserialization in Fortran" ) # Follow GNU conventions for installing directories include(GNUInstallDirs) include(CTest) # option(BUILD_TESTING). ON by default. # General configuration information add_subdirectory("config") # Collect source of the project set(srcs) add_subdirectory("src") # TOML-Fortran library target add_library( "${PROJECT_NAME}-lib" "${srcs}" ) set_target_properties( "${PROJECT_NAME}-lib" PROPERTIES POSITION_INDEPENDENT_CODE TRUE OUTPUT_NAME "${PROJECT_NAME}" VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}" Fortran_MODULE_DIRECTORY "${PROJECT_BINARY_DIR}/include" ) target_include_directories( "${PROJECT_NAME}-lib" PUBLIC $ $ $ ) if(NOT EXISTS "${PROJECT_BINARY_DIR}/include") file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/include") endif() # Export targets for other projects add_library("${PROJECT_NAME}" INTERFACE) target_link_libraries("${PROJECT_NAME}" INTERFACE "${PROJECT_NAME}-lib") install( TARGETS "${PROJECT_NAME}" "${PROJECT_NAME}-lib" EXPORT "${PROJECT_NAME}-targets" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) install( EXPORT "${PROJECT_NAME}-targets" NAMESPACE "${PROJECT_NAME}::" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) install( DIRECTORY "${PROJECT_BINARY_DIR}/include/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${module-dir}" ) # Package license files install( FILES "LICENSE-Apache" "LICENSE-MIT" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/${PROJECT_NAME}" ) # add the testsuite if(BUILD_TESTING) enable_testing() set(fpm-toml "${PROJECT_SOURCE_DIR}/fpm.toml") add_subdirectory("test") endif() fortran-toml-0.5.0/.github/0000775000175000017500000000000015201541453015664 5ustar alastairalastairfortran-toml-0.5.0/.github/workflows/0000775000175000017500000000000015201541453017721 5ustar alastairalastairfortran-toml-0.5.0/.github/workflows/docs.yml0000664000175000017500000000171015201541453021373 0ustar alastairalastairname: docs on: push: branches: - main pull_request: workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: pages-${{ github.ref }} cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies run: pip install ford - name: Build documentation run: ford docs.md -o _ford - name: Upload artifact if: github.event_name != 'pull_request' uses: actions/upload-pages-artifact@v3 with: path: _ford deploy: if: github.event_name != 'pull_request' needs: build runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 fortran-toml-0.5.0/.github/workflows/fortran-build.yml0000664000175000017500000001342615201541453023222 0ustar alastairalastairname: CI on: [push, pull_request] env: BUILD_DIR: _build PIP_PACKAGES: >- meson!=1.8.0 cmake ninja gcovr jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] build-type: [debug] toolchain: - {compiler: gcc, version: '12', build: meson} - {compiler: gcc, version: '12', build: cmake} include: - os: ubuntu-latest build-type: coverage toolchain: {compiler: gcc, version: '10', build: meson} - os: ubuntu-latest build-type: debug toolchain: {compiler: gcc, version: '11', build: meson} - os: ubuntu-latest build-type: debug toolchain: {compiler: gcc, version: '13', build: meson} - os: ubuntu-latest build-type: debug toolchain: {compiler: gcc, version: '14', build: meson} - os: ubuntu-latest build-type: debug toolchain: {compiler: gcc, version: '12', build: fpm} # - os: windows-latest # build: meson # build-type: debug # compiler: gcc # shell: msys2 {0} defaults: run: shell: bash env: PYTHON_V: 3.9 steps: - name: Checkout code uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_V }} - name: Install fpm if: ${{ matrix.toolchain.build == 'fpm' }} uses: fortran-lang/setup-fpm@v7 # fpm 0.11.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Prepare for cache restore if: ${{ contains(matrix.toolchain.compiler, 'intel') }} run: | sudo mkdir -p /opt/intel sudo chown $USER /opt/intel - name: Cache Intel installation if: ${{ contains(matrix.toolchain.compiler, 'intel') }} id: cache-install uses: actions/cache@v4 with: path: /opt/intel/oneapi key: install-${{ matrix.toolchain.compiler }}-${{ matrix.toolchain.version }}-${{ matrix.os }} - name: Install GCC Compiler if: ${{ contains(matrix.toolchain.compiler, 'gcc') }} uses: fortran-lang/setup-fortran@v1 with: compiler: ${{ matrix.toolchain.compiler }} version: ${{ matrix.toolchain.version }} - name: Install Intel Compiler (Linux) if: ${{ contains(matrix.os, 'ubuntu') && contains(matrix.toolchain.compiler, 'intel') && steps.cache-install.outputs.cache-hit != 'true' }} uses: fortran-lang/setup-fortran@v1 with: compiler: ${{ matrix.toolchain.compiler }} version: ${{ matrix.toolchain.version }} - name: Setup Intel oneAPI environment if: ${{ contains(matrix.toolchain.compiler, 'intel') }} run: | source /opt/intel/oneapi/setvars.sh --force printenv >> $GITHUB_ENV - name: Setup environment for Fortran and C compilers run: | if [ ! -n "$FC" ]; then if [ "${{ matrix.toolchain.compiler }}" = "gcc" ]; then FC="gfortran" CC="gcc" elif [ "${{ matrix.toolchain.compiler }}" = "intel" ]; then FC="ifx" CC="icx" elif [ "${{ matrix.toolchain.compiler }}" = "intel-classic" ]; then FC="ifort" CC="icc" fi echo "FC=$FC" >> $GITHUB_ENV echo "CC=$CC" >> $GITHUB_ENV fi - name: Install build and test dependencies if: ${{ ! contains(matrix.os, 'windows') }} run: pip3 install ${{ env.PIP_PACKAGES }} ${{ env.PIP_EXTRAS }} - name: Configure build (meson) if: ${{ matrix.toolchain.build == 'meson' }} run: >- meson setup ${{ env.BUILD_DIR }} --buildtype=debug --prefix=$PWD/_dist --libdir=lib --warnlevel=0 -Db_coverage=${{ env.COVERAGE }} ${{ env.MESON_ARGS }} env: COVERAGE: ${{ matrix.build-type == 'coverage' }} MESON_ARGS: >- ${{ contains(matrix.os, 'windows') && '-Dfortran_link_args=-Wl,--allow-multiple-definition' || '' }} - name: Configure build (CMake) if: ${{ matrix.toolchain.build == 'cmake' }} run: >- cmake -B${{ env.BUILD_DIR }} -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$PWD/_dist -DCMAKE_INSTALL_LIBDIR=lib - name: Build library (fpm) if: ${{ matrix.toolchain.build == 'fpm' }} run: | fpm --version fpm build - name: Build library if: ${{ matrix.toolchain.build != 'fpm' }} run: ninja -C ${{ env.BUILD_DIR }} - name: Run unit tests (fpm) if: ${{ matrix.toolchain.build == 'fpm' }} run: fpm test - name: Run unit tests (meson) if: ${{ matrix.toolchain.build == 'meson' }} run: >- meson test -C ${{ env.BUILD_DIR }} --print-errorlogs --no-rebuild --num-processes 1 --suite toml-f -t 2 env: OMP_NUM_THREADS: 1,2,1 - name: Run unit tests (ctest) if: ${{ matrix.toolchain.build == 'cmake' }} run: | ctest --output-on-failure --parallel 2 -R '^toml-f/' working-directory: ${{ env.BUILD_DIR }} env: OMP_NUM_THREADS: 1,2,1 - name: Create coverage report if: ${{ matrix.toolchain.build == 'meson' && matrix.build-type == 'coverage' }} run: ninja -C ${{ env.BUILD_DIR }} coverage - name: Install project if: ${{ matrix.toolchain.build != 'fpm' }} run: | ninja -C ${{ env.BUILD_DIR }} install echo "TOMLF_PREFIX=$PWD/_dist" >> $GITHUB_ENV - name: Upload coverage report if: ${{ matrix.toolchain.build == 'meson' && matrix.build-type == 'coverage' }} uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} fortran-toml-0.5.0/.github/dco.yml0000664000175000017500000000003215201541453017147 0ustar alastairalastairrequire: members: false