From b61cb570dcc898dd895e2e2afeaafcef40077a9a Mon Sep 17 00:00:00 2001 From: aheinri5 <Anna@netzkritzler.de> Date: Wed, 18 Nov 2020 23:10:18 +0100 Subject: [PATCH] New script to collect functions that generate functions (functionception) --- R/generate_functions.R | 255 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 R/generate_functions.R diff --git a/R/generate_functions.R b/R/generate_functions.R new file mode 100644 index 0000000..70c622c --- /dev/null +++ b/R/generate_functions.R @@ -0,0 +1,255 @@ +#This is a script containing experimental functions which generate other functions. +#These will not be exported, but in the best case developers can use them to make their workflow more efficient. + +#Experimental stuff +generate_from_element <- function(element_name, attrs, children) { + + +} + + + +generate_class_from_element <- function(element, export = TRUE) { + + children <- xml2::xml_children(element) + attrs <- xml2::xml_attrs(element) + element_name <- xml2::xml_name(element) + + #Generate name of new class from element name if class_name is not specified + if(is.null(class_name)){ + class_name <- snakecase::to_any_case(element_name, "snake") + } + + + #Start of description + class_str <- paste0("#'r2ogs6_", element_name, "\n", + "#'@description r2ogs6 class representing a ", element_name, " element \n") + + #Add parameter documentation tag(s) + parameters <- list() + + for(i in seq_len(length(children))){ + parameters <- c(parameters, xml2::xml_name(children[[i]])) + + class_str <- paste0(class_str, "#'@param ", xml2::xml_name(children[[i]]), " (Auto generated)\n") + } + + #Add export documentation tag if class should be exported + if(export){ + class_str <- paste0(class_str, "#'@export\n") + } + + #Start of declaration + class_str <- paste0(class_str, "r2ogs6_", element_name, " <- function(\n") + + #ADD PARAMETERS HERE + + class_str <- paste0(class_str, ") {\n") + + #ADD FUNCTION CONTENTS + + class_str <- paste0(class_str, "}\n") + + + generated_class <- structure(list(), class = element_name) + + return(generated_class) +} + +generate_validator_from_element <- function() { + +} + + +#============================== AS_NODE METHOD GENERATOR ================================ + + +#'generate_as_node_func +#'@description Generates a method for the generic function as_node based on an XML element +#'@param element_name The name of the XML element to base the method on +#'@param subclasses Optional: A named vector of subclasses +generate_as_node_func <- function(path, element_name, subclasses = NULL) { + + out<- tryCatch( + { + xml_doc <- xml2::read_xml(path, encoding="ISO-8859-1") + }, + + error = function(cond){ + cat("Something went wrong while parsing the XML file for generate_as_node_func.\n") + } + ) + + #doc_matches <- xml2::xml_find_all(xml_doc, paste("//", element_name, sep = "")) + element <- xml2::xml_find_first(xml_doc, paste("//", element_name, sep = "")) + element_attrs <- xml2::xml_attrs(element) + + method_str <- paste0("#'as_node.r2ogs6_", element_name, "\n", + "#'@description Implementation of generic as_node for class r2ogs6_", element_name, "\n", + "#'@param x A r2ogs6_", element_name, " class object\n", + "#'@param ... Ellipsis\n", + "as_node.r2ogs6_", element_name, " <- function(x, ...) {\n") + + method_str <- paste0(method_str, generate_as_node_method_content(element, element_attrs)) + + method_str <- paste0(method_str, "\treturn(invisible(", element_name, "_node))\n", "}\n") + + cat(method_str) + + return(invisible(method_str)) +} + + +#'generate_as_node_method_content +#'@description ... +#'@param element The XML element to base the method on +#'@param iteration_depth Utility parameter for the recursion, just leave this alone! +#'@param subclasses Optional: A named vector of subclasses +generate_as_node_method_content <- function(element, iteration_depth = 0, subclasses = NULL) { + + children <- xml2::xml_children(element) + attrs <- xml2::xml_attrs(element) + element_name <- xml2::xml_name(element) + + if(length(children) == 0){ + #return(paste0("\t", element_name, "_node <- as_node(x, )\n")) + + return_str <- paste0("\t", element_name, "_node <- as_node(x$", element_name) + + if(length(attrs) != 0){ + return_str <- paste0(return_str, ", '', ", attrs) + } + + return_str <- paste0(return_str, ")\t#atomic\n") + return(return_str) + } + + child_strings <- list() + + for(i in seq_len(length(children))){ + child_strings[[length(child_strings) + 1]] <- + generate_as_node_method_content(children[[i]], (iteration_depth + 1), subclasses) + } + + if(strings_equal(child_strings)){ + return_str <- paste0("\t", element_name, "_node <- group_nodes(x$", element_name) + + if(length(attrs) != 0){ + return_str <- paste0(return_str, ", ", attrs) + } + + return_str <- paste0(return_str, ")\n") + + return(return_str) + } + +} + + +#'strings_equal +#'@description Checks if all strings in a vector or list are equal +#'@param strings_vector A vector or a list of strings +strings_equal <- function(strings_vector) { + assertthat::assert_that(is.vector(strings_vector)) + + for(i in seq_len(length(strings_vector))){ + if(strings_vector[[i]] != strings_vector[[1]]){ + return(invisible(FALSE)) + } + } + + return(invisible(TRUE)) +} + + +#... (WIP) +group_nodes <- function(parent_list, parent_attributes = NULL, child_attributes = NULL) { + + assertthat::assert_that(is.list(parent_list)) + + #Deparses the parameter given to the function + parameter_name <- deparse(substitute(parent_list)) + + #Automatically generates the element name based on the name of the parameter + parent_name <- strsplit(parameter_name, "$", fixed = TRUE)[[2]] + + if(!is.null(parent_attributes)){ + assertthat::assert_that(is.vector(parent_attributes)) + } + + node <- list(structure(list(), parent_attributes)) + names(node)[[1]] <- parent_name + + for(i in seq_len(length(parent_list))){ + node[[1]][[length(node[[1]] + 1)]] <- as_node(parent_list[[i]]) + } + + return(invisible(node)) +} + + +#============================== READ_IN FUNCTION GENERATOR ================================ + +#'generate_simple_read_in +#'@description Assuming function read_in gets good enough results, this could save code later. +#'@param element_name The name of the .prj element to be read from (wrapper element, e.g. 'processes') +#'@param child_name The name of the element children (e.g. 'process') +#'@param has_name_tag Do the child elements have a child element with the name 'name'? +#'@param subclasses_names Optional: A character vector containing the names of r2ogs6_* +#' subclasses (r2ogs6_* classes without a method for input_add) +generate_simple_read_in <- function(element_name, child_name, + has_name_tag = TRUE, subclasses_names = NULL){ + + assertthat::assert_that(assertthat::is.string(element_name)) + assertthat::assert_that(assertthat::is.string(child_name)) + assertthat::assert_that(assertthat::is.flag(has_name_tag)) + + if(!is.null(subclasses_names)){ + assertthat::assert_that(is.character(subclasses_names)) + } + + + func_str <- paste0("#'read_in_", element_name, "\n", + "#'@description Reads in ", child_name, " elements from a .prj file\n", + "#'@param ogs6_obj A OGS6 class object\n", + "#'@param prj_path The path to the project file the ", child_name, + " elements should be read from\n") + + if(has_name_tag){ + func_str <- paste0(func_str, "#'@param ", child_name, "_names Optional: The names of the ", + child_name, " elements to be read in\n") + }else{ + func_str <- paste0(func_str, "#'@param ", child_name, "_indices Optional: The indices of the ", + child_name, " elements to be read in\n") + } + + func_str <- paste0(func_str, "#'@export\n", + "read_in_", element_name, " <- function(ogs6_obj, prj_path, ") + + if(has_name_tag){ + func_str <- paste0(func_str, child_name, "_names = NULL) {\n") + }else{ + func_str <- paste0(func_str, child_name, "_indices = NULL) {\n") + } + + func_str <- paste0(func_str, "read_in(ogs6_obj, prj_path, \"", element_name, + "\", \"", child_name, "\", has_name_tag = ", has_name_tag, + "selection_vector = ") + + if(has_name_tag){ + func_str <- paste0(func_str, child_name, "_names, subclasses_names = ") + }else{ + func_str <- paste0(func_str, child_name, "_indices, subclasses_names = ") + } + + if(!is.null(subclasses_names)){ + func_str <- paste0(func_str, dput(subclasses_names), ")\n") + }else{ + func_str <- paste0(func_str, "NULL)\n") + } + + func_str <- paste0(func_str, "}\n") + + cat(func_str) + return(invisible(func_str)) +} \ No newline at end of file -- GitLab