function(bison_target_or_gen grammar)
    get_filename_component(directory "${grammar}" DIRECTORY)
    get_filename_component(filename "${grammar}" NAME_WE)
    get_filename_component(extension "${grammar}" EXT)
    set(gen_path "${CMAKE_CURRENT_SOURCE_DIR}/gen/${directory}/${filename}")
    set(bin_path "${CMAKE_CURRENT_BINARY_DIR}/${directory}/${filename}")
    file(MAKE_DIRECTORY "${bin_path}")
    if(BISON_FOUND)
        bison_target("${filename}" "${CMAKE_CURRENT_SOURCE_DIR}/${grammar}" "${bin_path}/grammar.cc")
        if(MSVC)
            set_source_files_properties("${BISON_${filename}_OUTPUT_SOURCE}"
                PROPERTIES COMPILE_FLAGS "/wd4267 /wd4065")
        endif()
        set("BISON_${filename}_OUTPUT_SOURCE" "${BISON_${filename}_OUTPUT_SOURCE}" PARENT_SCOPE)
        set("BISON_${filename}_OUTPUTS" ${BISON_${filename}_OUTPUTS} PARENT_SCOPE)
    elseif (EXISTS "${gen_path}")
        file(COPY "${gen_path}/" DESTINATION "${bin_path}" FILES_MATCHING PATTERN "*.*")
        set("BISON_${filename}_OUTPUT_SOURCE" "${bin_path}/grammar.cc" PARENT_SCOPE)
        set("BISON_${filename}_OUTPUTS" "${bin_path}/grammar.cc" "${bin_path}/grammar.hh" PARENT_SCOPE)
    else()
        message(FATAL_ERROR "bison parser generator required but not found")
    endif()
endfunction()

function(re2c_target_or_gen grammar)
    get_filename_component(directory "${grammar}" DIRECTORY)
    get_filename_component(filename "${grammar}" NAME_WE)
    get_filename_component(extension "${grammar}" EXT)
    set(bin_path "${CMAKE_CURRENT_BINARY_DIR}/${directory}")
    set(gen_path "${CMAKE_CURRENT_SOURCE_DIR}/gen/${directory}")
    file(MAKE_DIRECTORY "${bin_path}")
    if(RE2C_FOUND)
        if (extension STREQUAL ".xch")
            re2c_target(NAME ${filename} INPUT "${CMAKE_CURRENT_SOURCE_DIR}/${grammar}" OUTPUT "${bin_path}/${filename}.hh" OPTIONS -c)
        else()
            re2c_target(NAME ${filename} INPUT "${CMAKE_CURRENT_SOURCE_DIR}/${grammar}" OUTPUT "${bin_path}/${filename}.hh")
        endif()
        set("RE2C_${filename}_OUTPUT" ${RE2C_${filename}_OUTPUT} PARENT_SCOPE)
    elseif (EXISTS "${gen_path}/${filename}.hh")
        file(COPY "${gen_path}/${filename}.hh" DESTINATION "${bin_path}")
        set("RE2C_${filename}_OUTPUT" "${bin_path}/${filename}.hh" PARENT_SCOPE)
    else()
        message(FATAL_ERROR "re2c lexer generator required but not found")
    endif()
endfunction()
