automerge/rust/automerge-c/cmake/enum-string-functions-gen.cmake
Jason Kankiewicz 8de2fa9bd4
C API 2 (#530)
The AMvalue union, AMlistItem struct, AMmapItem struct, and AMobjItem struct are gone, replaced by the AMitem struct.

The AMchangeHashes, AMchanges, AMlistItems, AMmapItems, AMobjItems, AMstrs, and AMsyncHaves iterators are gone, replaced by the AMitems iterator.

The AMitem struct is opaque, getting and setting values is now achieved exclusively through function calls.

The AMitemsNext(), AMitemsPrev(), and AMresultItem() functions return a pointer to an AMitem struct so you ultimately get the same thing whether you're iterating over a sequence or calling AMmapGet() or AMlistGet().

Calling AMitemResult() on an AMitem struct will produce a new AMresult struct referencing its storage so now the AMresult struct for an iterator can be subsequently freed without affecting the AMitem structs that were filtered out of it.

The storage for a set of AMitem structs can be recombined into a single AMresult struct by passing pointers to their corresponding AMresult structs to AMresultCat().

For C/C++ programmers, I've added AMstrCmp(), AMstrdup(), AM{idxType,objType,status,valType}ToString() and AM{idxType,objType,status,valType}FromString(). It's also now possible to pass arbitrary parameters through AMstack{Item,Items,Result}() to a callback function.
2023-02-25 18:47:00 +00:00

183 lines
7.1 KiB
CMake

# This CMake script is used to generate a header and a source file for utility
# functions that convert the tags of generated enum types into strings and
# strings into the tags of generated enum types.
cmake_minimum_required(VERSION 3.23 FATAL_ERROR)
# Seeks the starting line of the source enum's declaration.
macro(seek_enum_mode)
if (line MATCHES "^(typedef[ \t]+)?enum ")
string(REGEX REPLACE "^enum ([0-9a-zA-Z_]+).*$" "\\1" enum_name "${line}")
set(mode "read_tags")
endif()
endmacro()
# Scans the input for the current enum's tags.
macro(read_tags_mode)
if(line MATCHES "^}")
set(mode "generate")
elseif(line MATCHES "^[A-Z0-9_]+.*$")
string(REGEX REPLACE "^([A-Za-z0-9_]+).*$" "\\1" tmp "${line}")
list(APPEND enum_tags "${tmp}")
endif()
endmacro()
macro(write_header_file)
# Generate a to-string function declaration.
list(APPEND header_body
"/**\n"
" * \\ingroup enumerations\n"
" * \\brief Gets the string representation of an `${enum_name}` enum tag.\n"
" *\n"
" * \\param[in] tag An `${enum_name}` enum tag.\n"
" * \\return A null-terminated byte string.\n"
" */\n"
"char const* ${enum_name}ToString(${enum_name} const tag)\;\n"
"\n")
# Generate a from-string function declaration.
list(APPEND header_body
"/**\n"
" * \\ingroup enumerations\n"
" * \\brief Gets an `${enum_name}` enum tag from its string representation.\n"
" *\n"
" * \\param[out] dest An `${enum_name}` enum tag pointer.\n"
" * \\param[in] src A null-terminated byte string.\n"
" * \\return `true` if \\p src matches the string representation of an\n"
" * `${enum_name}` enum tag, `false` otherwise.\n"
" */\n"
"bool ${enum_name}FromString(${enum_name}* dest, char const* const src)\;\n"
"\n")
endmacro()
macro(write_source_file)
# Generate a to-string function implementation.
list(APPEND source_body
"char const* ${enum_name}ToString(${enum_name} const tag) {\n"
" switch (tag) {\n"
" default:\n"
" return \"???\"\;\n")
foreach(label IN LISTS enum_tags)
list(APPEND source_body
" case ${label}:\n"
" return \"${label}\"\;\n")
endforeach()
list(APPEND source_body
" }\n"
"}\n"
"\n")
# Generate a from-string function implementation.
list(APPEND source_body
"bool ${enum_name}FromString(${enum_name}* dest, char const* const src) {\n")
foreach(label IN LISTS enum_tags)
list(APPEND source_body
" if (!strcmp(src, \"${label}\")) {\n"
" *dest = ${label}\;\n"
" return true\;\n"
" }\n")
endforeach()
list(APPEND source_body
" return false\;\n"
"}\n"
"\n")
endmacro()
function(main)
set(header_body "")
# File header and includes.
list(APPEND header_body
"#ifndef ${include_guard}\n"
"#define ${include_guard}\n"
"/**\n"
" * \\file\n"
" * \\brief Utility functions for converting enum tags into null-terminated\n"
" * byte strings and vice versa.\n"
" *\n"
" * \\warning This file is auto-generated by CMake.\n"
" */\n"
"\n"
"#include <stdbool.h>\n"
"\n"
"#include <${library_include}>\n"
"\n")
set(source_body "")
# File includes.
list(APPEND source_body
"/** \\warning This file is auto-generated by CMake. */\n"
"\n"
"#include \"stdio.h\"\n"
"#include \"string.h\"\n"
"\n"
"#include <${header_include}>\n"
"\n")
set(enum_name "")
set(enum_tags "")
set(mode "seek_enum")
file(STRINGS "${input_path}" lines)
foreach(line IN LISTS lines)
string(REGEX REPLACE "^(.+)(//.*)?" "\\1" line "${line}")
string(STRIP "${line}" line)
if(mode STREQUAL "seek_enum")
seek_enum_mode()
elseif(mode STREQUAL "read_tags")
read_tags_mode()
else()
# The end of the enum declaration was reached.
if(NOT enum_name)
# The end of the file was reached.
return()
endif()
if(NOT enum_tags)
message(FATAL_ERROR "No tags found for `${enum_name}`.")
endif()
string(TOLOWER "${enum_name}" output_stem_prefix)
string(CONCAT output_stem "${output_stem_prefix}" "_string")
cmake_path(REPLACE_EXTENSION output_stem "h" OUTPUT_VARIABLE output_header_basename)
write_header_file()
write_source_file()
set(enum_name "")
set(enum_tags "")
set(mode "seek_enum")
endif()
endforeach()
# File footer.
list(APPEND header_body
"#endif /* ${include_guard} */\n")
message(STATUS "Generating header file \"${output_header_path}\"...")
file(WRITE "${output_header_path}" ${header_body})
message(STATUS "Generating source file \"${output_source_path}\"...")
file(WRITE "${output_source_path}" ${source_body})
endfunction()
if(NOT DEFINED PROJECT_NAME)
message(FATAL_ERROR "Variable PROJECT_NAME is not defined.")
elseif(NOT DEFINED LIBRARY_NAME)
message(FATAL_ERROR "Variable LIBRARY_NAME is not defined.")
elseif(NOT DEFINED SUBDIR)
message(FATAL_ERROR "Variable SUBDIR is not defined.")
elseif(${CMAKE_ARGC} LESS 9)
message(FATAL_ERROR "Too few arguments.")
elseif(${CMAKE_ARGC} GREATER 10)
message(FATAL_ERROR "Too many arguments.")
elseif(NOT EXISTS ${CMAKE_ARGV5})
message(FATAL_ERROR "Input header \"${CMAKE_ARGV7}\" not found.")
endif()
cmake_path(CONVERT "${CMAKE_ARGV7}" TO_CMAKE_PATH_LIST input_path NORMALIZE)
cmake_path(CONVERT "${CMAKE_ARGV8}" TO_CMAKE_PATH_LIST output_header_path NORMALIZE)
cmake_path(CONVERT "${CMAKE_ARGV9}" TO_CMAKE_PATH_LIST output_source_path NORMALIZE)
string(TOLOWER "${PROJECT_NAME}" project_root)
cmake_path(CONVERT "${SUBDIR}" TO_CMAKE_PATH_LIST project_subdir NORMALIZE)
string(TOLOWER "${project_subdir}" project_subdir)
string(TOLOWER "${LIBRARY_NAME}" library_stem)
cmake_path(REPLACE_EXTENSION library_stem "h" OUTPUT_VARIABLE library_basename)
string(JOIN "/" library_include "${project_root}" "${library_basename}")
string(TOUPPER "${PROJECT_NAME}" project_name_upper)
string(TOUPPER "${project_subdir}" include_guard_infix)
string(REGEX REPLACE "/" "_" include_guard_infix "${include_guard_infix}")
string(REGEX REPLACE "-" "_" include_guard_prefix "${project_name_upper}")
string(JOIN "_" include_guard_prefix "${include_guard_prefix}" "${include_guard_infix}")
string(JOIN "/" output_header_prefix "${project_root}" "${project_subdir}")
cmake_path(GET output_header_path STEM output_header_stem)
string(TOUPPER "${output_header_stem}" include_guard_stem)
string(JOIN "_" include_guard "${include_guard_prefix}" "${include_guard_stem}" "H")
cmake_path(GET output_header_path FILENAME output_header_basename)
string(JOIN "/" header_include "${output_header_prefix}" "${output_header_basename}")
main()