refactoring class Graph, updating project structure
authorThomas Walker Lynch <xtujpz@reasoningtechnology.com>
Sun, 13 Oct 2024 05:18:13 +0000 (05:18 +0000)
committerThomas Walker Lynch <xtujpz@reasoningtechnology.com>
Sun, 13 Oct 2024 05:19:22 +0000 (05:19 +0000)
44 files changed:
developer/deprecated/#Ariandne.groovy# [deleted file]
developer/deprecated/Ariandne2.groovy [new file with mode: 0644]
developer/deprecated/Graph.java [new file with mode: 0644]
developer/deprecated/Label.java [new file with mode: 0644]
developer/deprecated/Token.java [new file with mode: 0644]
developer/javac/File.java [new file with mode: 0644]
developer/javac/Graph.java [deleted file]
developer/javac/Graph.javax [new file with mode: 0644]
developer/javac/Label.java [deleted file]
developer/javac/Token.java [deleted file]
developer/shell/build [deleted file]
developer/tool/clean [deleted file]
developer/tool/clean_all [deleted file]
developer/tool/clean_dist [deleted file]
developer/tool/clean_javac [new file with mode: 0755]
developer/tool/clean_make [new file with mode: 0755]
developer/tool/clean_release [new file with mode: 0755]
developer/tool/env
developer/tool/make
developer/tool/release
document/directory_naming.html
release/.githolder [new file with mode: 0644]
release/black [new file with mode: 0755]
release/blue [new file with mode: 0755]
release/green [new file with mode: 0755]
release_candidate/.githolder [deleted file]
release_candidate/black [deleted file]
release_candidate/blue [deleted file]
release_candidate/green [deleted file]
tester/javac/TestBench.java [new file with mode: 0644]
tester/tool/env [new file with mode: 0644]
tester/tool/env_tester [deleted file]
tester/tool/make [new file with mode: 0755]
tester/tool/make.sh [deleted file]
tool/env [new file with mode: 0644]
tool/env_administrator [deleted file]
tool/env_base [deleted file]
tool/env_developer [deleted file]
tool/env_tester [deleted file]
tool_shared/bespoke/deprecate [new file with mode: 0755]
tool_shared/bespoke/env [new file with mode: 0644]
tool_shared/bespoke/temp [new file with mode: 0644]
tool_shared/bespoke/version
tool_shared/bespoke/wipe_replease [new file with mode: 0755]

diff --git a/developer/deprecated/#Ariandne.groovy# b/developer/deprecated/#Ariandne.groovy#
deleted file mode 100644 (file)
index 6c0cf17..0000000
+++ /dev/null
@@ -1,635 +0,0 @@
-class AriadneGraph {
-
-  // Instance variables for graph data if needed
-  Map node_map = [:]
-  List node_f_list = []
-
-  // Constructor to accept a graph definition (node_map and node_f_list)
-  AriadneGraph( Map node_map ,List node_f_list ){
-    this.node_map = node_map ?: [:]
-    this.node_f_list = node_f_list ?: []
-  }
-
-  /*--------------------------------------------------------------------------------
-   File utility functions
-  */
-
-  static Map unpack_file_path( String file_fp ){
-    def file = new File( file_fp )
-
-    def parent_dp = file.getParent()
-    def file_fn = file.getName()
-    def file_fn_base = file_fn.lastIndexOf('.') > 0 ? file_fn[ 0..file_fn.lastIndexOf('.') - 1 ] : file_fn
-    def file_fn_ext = file_fn.lastIndexOf('.') > 0 ? file_fn[ file_fn.lastIndexOf('.') + 1..-1 ] : ''
-
-    return [
-      parent_dp: parent_dp
-      ,file_fn: file_fn
-      ,file_fn_base: file_fn_base
-      ,file_fn_ext: file_fn_ext
-    ]
-  }
-
-  static boolean file_exists_q( String node_label ){
-    def node_path = Paths.get( node_label )
-    return Files.exists( node_path )
-  }
-
-  /*--------------------------------------------------------------------------------
-   Node type checks and marking
-  */
-
-  static Set all_node_type_set = ['symbol' ,'path' ,'leaf' ,'generator'] as Set
-  static Set persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as Set
-
-  static boolean leaf_q( Map node ){
-    return node && node.type == 'leaf'
-  }
-
-  static boolean has_mark( Map node ){
-    return node?.mark?.isNotEmpty()
-  }
-
-  static void set_mark( Map node ,String mark ){
-    node.mark = node.mark ?: [] as Set
-    node.mark << mark
-  }
-
-  static void clear_mark( Map node ,String mark ){
-    node?.mark?.remove( mark )
-  }
-
-  static boolean marked_good_q( Map node ){
-    return node && node.mark && ( 'wellformed' in node.mark ) && !( 'cycle_member' in node.mark ) && !( 'build_failed' in node.mark )
-  }
-
-  /*--------------------------------------------------------------------------------
-   Well-formed Node Check
-  */
-
-  static Set all_form_error_set = [
-    'no_node'
-    ,'node_must_have_label'
-    ,'label_must_be_string'
-    ,'node_must_have_type'
-    ,'bad_node_type'
-    ,'neighbor_value_must_be_list'
-    ,'neighbor_reference_must_be_string'
-    ,'mark_property_value_must_be_set'
-    ,'unregistered_mark'
-    ,'missing_required_build_code'
-    ,'leaf_given_neighbor_property'
-    ,'leaf_given_build_property'
-  ] as Set
-
-  static Set wellformed_q( Map node ){
-    def form_error_set = [] as Set
-
-    if( !node ){
-      form_error_set << 'no_node'
-      return form_error_set
-    }
-
-    if( !node.label )
-      form_error_set << 'node_must_have_label'
-    else if( !( node.label instanceof String ) )
-      form_error_set << 'label_must_be_string'
-
-    if( !node.type )
-      form_error_set << 'node_must_have_type'
-    else if( !( node.type instanceof String ) || !( node.type in all_node_type_set ) )
-      form_error_set << 'bad_node_type'
-
-    if( node.neighbor ){
-      if( !( node.neighbor instanceof List ) )
-        form_error_set << 'neighbor_value_must_be_list'
-      else if( !( node.neighbor.every { it instanceof String } ) )
-        form_error_set << 'neighbor_reference_must_be_string'
-    }
-
-    if( node.mark ){
-      if( !( node.mark instanceof Set ) )
-        form_error_set << 'mark_property_value_must_be_set'
-      else if( !( node.mark.every { it in persistent_node_mark_set } ) )
-        form_error_set << 'unregistered_mark'
-    }
-
-    if( node.type == 'path' && ( !node.build || !( node.build instanceof Closure ) ) )
-      form_error_set << 'missing_required_build_code'
-
-    if( node.type == 'leaf' ){
-      if( node.neighbor ) form_error_set << 'leaf_given_neighbor_property'
-      if( node.build ) form_error_set << 'leaf_given_build_property'
-    }
-
-    return form_error_set
-  }
-
-  /*--------------------------------------------------------------------------------
-   Graph traversal and build functions
-  */
-
-  def lookup( String node_label ,boolean verbose = false ){
-    def lookup_node = node_map[ node_label ]
-    if( !lookup_node ){
-      def match_result
-      for( func in node_f_list ){
-        match_result = func( node_label )
-        if( match_result.status == "matched" ){
-          lookup_node = match_result
-          break
-        }
-      }
-    }
-
-    if( !lookup_node && verbose ) println "lookup:: Node ${node_label} could not be found."
-    return lookup_node
-  }
-
-  def run_build_scripts_f( List root_node_labels ,boolean verbose = true ){
-    if( root_node_labels.isEmpty() ) return
-
-    def node_function = { node ,error_token_set ->
-
-      if( !can_be_built_q( node ) ){
-        println( "Skipping build for ${node.label} due to dependency problems" )
-        return
-      }
-      if( !should_be_built_q( node ) ){
-        if( verbose ) println( "${node.label} already up to date" )
-        return
-      }
-
-      println( "Running build script for ${node.label}" )
-      node.build( node ,node.neighbor )
-
-      if( should_be_built_q( node ) ){
-        println( "Build failed for ${node.label}" )
-        set_mark( node ,'build_failed' )
-      }
-    }
-
-    println( "run_build_scripts_f:: running ..." )
-    all_DAG_DF( root_node_labels ,node_function ,verbose )
-  }
-
-  // Add the rest of your methods here as instance/static methods based on whether they depend on the graph instance
-
-}
-/*
- Some terms:
-
- a node is either malformed or 'wellformed'.  A wellformed node meets
- the criteria set forth by `well_formed_node_q`.
-
- 'node': a dictionary, hopefully a wellformed one
- 'file node': a 'path' or 'leaf' type node.
- 'node file': for 'path' or 'leaf' nodes, path relative to the developer's directory.
-
- This code makes use of the following node properties:
- - label: Unique label string. Used to reference nodes.
- - type: The type of the node: 'leaf' ,'symbol' ,or 'path'.
- - neighbor: list of the neighbor nodes labels
- - must_have: list of node labels
- - build: Code that builds the node.
- - mark: a set of 'mark' tokens optionally placed on a node
-
- For 'path' and 'leaf' type nodes, the node label is a file path.  Relative paths
- are relative to the developer's directory.
-
- The dependency graph is defined by the `lookup` function. `lookup` when given
- a node label returns the node, if found, or null.
-
-*/
-
-/*--------------------------------------------------------------------------------
- file utility functions
-*/
-
-def unpack_file_path = { file_fp ->
-  def file = new File(file_fp)
-
-  // Get the parent directory path
-  def parent_dp = file_fn.getParent()
-
-  // Get the file name (with extension)
-  def file_fn = file_fn.getName()
-
-  // Split the file name into base and extension
-  def file_fn_base = file_fn.lastIndexOf('.') > 0 ? file_fn[0..file_fn.lastIndexOf('.') - 1] : file_fn
-  def file_fn_ext = file_fn.lastIndexOf('.') > 0 ? file_fn[file_fn.lastIndexOf('.') + 1..-1] : ''
-
-  // Return the components as a map for easier use
-  return [
-    parent_dp: parent_dp
-    ,file_fn: file_fn
-    ,file_fn_base: file_fn_base
-    ,file_fn_ext: file_fn_ext
-  ]
-}
-
-def file_exists_q(node_label) {
-  def node_path = Paths.get(node_label)
-  return Files.exists(node_path)
-}
-
-
-/*-------------------------------------------------------------------------------
-  DAG adjectives
-
-  node undefined for undefined node?
-*/
-
-// A node is type 'leaf' when the node file can not be deleted or replaced.
-// This type used to be called 'primary', which might be a better term.
-
-def all_node_type_set = ['symbol' ,'path' ,'leaf' ,'generator'] as Set
-
-def persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as Set
-def leaf_q = { node -> node && node.type && node.type == 'leaf' }
-
-// mark 
-def has_mark = { node -> node.mark && !node.mark.isEmpty() }
-def set_mark(node ,mark){
-  node.mark = node.mark ?: [] as Set
-  node.mark << mark
-}
-def clear_mark(node ,mark){
-  if( node.mark ) node.mark.remove(mark)
-}
-def marked_good_q(node){
-  return (
-    node 
-    && node.mark 
-    && ('wellformed' in node.mark) 
-    && !('cycle_member' in node.mark)
-    && !('build_failed' in node.mark)
-  )
-}
-
-
-
-/*--------------------------------------------------------------------------------
- Wellformed Node Check
-
- The developer defines the nodes, so their form needs to be checked.
-
- early bail on node not defined
- legal path in the build directory makedir -p ... not really a acquirement 
-
-*/
-def all_form_error_set = [
-  'no_node'
-  ,'node_must_have_label'
-  ,'label_must_be_string'
-  ,'node_must_have_type'
-  ,'bad_node_type'
-  ,'neighbor_value_must_be_list'
-  ,'neighbor_reference_must_be_string'
-  ,'mark_property_value_must_be_set'
-  ,'unregistered_mark'
-  ,'missing_required_build_code'
-  ,'leaf_given_neighbor_property'
-  ,'leaf_given_build_property'
-]
-def wellformed_q = { node -> 
-  def form_error_set = [] as Set
-
-  if( !node ){
-    form_error_set << 'no_node'
-    return form_error_set
-  }
-
-  if( !node.label )
-    form_error_set << 'node_must_have_label'
-  else if( !(node.label instanceof String) )
-    form_error_set << 'label_must_be_string'
-
-  if( !node.type )
-    form_error_set << 'node_must_have_type'
-  else if( !(node.type instanceof String) || !(node.type in all_node_type_set) )
-    form_error_set << 'bad_node_type'
-    
-  if( node.neighbor ){
-    if( !(node.neighbor instanceof List) )
-      form_error_set << 'neighbor_value_must_be_list'
-    else if( !(node.neighbor.every { it instanceof String }) )
-      form_error_set << 'neighbor_reference_must_be_string'
-  }
-
-  if( node.mark ){
-    if( !(node.mark instanceof Set) ) 
-      form_error_set << 'mark_property_value_must_be_set'
-    else if( !(node.mark.every { it in persistent_node_mark_set }) )
-      form_error_set << 'unregistered_mark'
-  }
-
-  // Removed test about symbol build needing neighbors
-
-  if( 
-    node.type == "path" 
-    && (!node.build || !(node.build instanceof Closure))
-  )
-    form_error_set << 'missing_required_build_code'
-
-  // Removed test about path needing neighbors
-
-  // Can a primary file have neighbors or a build script? Our build model
-  // has the node file as a target, the build script as a means of building
-  // the target (hence its name), and the dependencies as inputs to the build
-  // script. So in the spirit of this model, we will say "no".
-  if( node.type == "leaf" ){
-    if( node.neighbor ) form_error_set << 'leaf_given_neighbor_property'
-    if( node.build ) form_error_set << 'leaf_given_build_property'
-  }
-
-  return form_error_set
-}
-
-
-
-/*--------------------------------------------------------------------------------
- A well formed graph checker.  Traverses entire graph and marks nodes
- that are not well formed or that are part of a cycle.
-
-*/
-
-// given a node label list, adds the 'wellformed' mark to wellformed nodes.
-def mark_the_wellformed_f(node_label_list ,boolean verbose = true){
-  def all_wellformed = true
-
-  def neighbors = node_label_list.collect{ neighbor_label ->
-    def neighbor_node = lookup(neighbor_label)
-    def form_errors = wellformed_q(neighbor_node)
-    if(form_errors.isEmpty()){
-      neighbor_node.mark = neighbor_node.mark ?: [] as Set
-      neighbor_node.mark << 'wellformed'
-    } else {
-      all_wellformed = false
-      if(verbose){
-        if(neighbor_node.label && neighbor_node.label.length() > 0){
-          print("node ${neighbor_node.label} is malformed due to:")
-        } else {
-          print("anonymous node is malformed due to:")
-        }
-        form_errors.each { error -> print(" ${error}") }
-        println("")
-      }
-    }
-    neighbor_label
-  }
-
-  return all_wellformed ? 'all_wellformed' : 'exists_malformed'
-}
-
-// descends un-visited leftmost path, while marking nodes as wellformed and if
-// they are part of a cycle.
-def markup_graph_f_descend(path_stack ,boolean verbose = true){
-  def ret_value = [] as Set
-  def local_path = path_stack.collect{ it[0] }
-  def local_node_label = local_path[-1]
-  def cycle_start_index
-
-  do{
-    // Check for a cycle in the local path
-    cycle_start_index = local_path[0..-2].findIndexOf{ it == local_node_label }
-    if(cycle_start_index != -1){ // Cycle detected
-      ret_value << 'cycle_found'
-      if(verbose) print "markup_graph_f_descend:: dependency cycle found:"
-      local_path[cycle_start_index..-1].each{ cycle_node_label ->
-        def cycle_node = lookup(cycle_node_label)
-        if(verbose) print " ${cycle_node.label}"
-        cycle_node.mark = cycle_node.mark ?: [] as Set // Initialize mark set if needed
-        cycle_node.mark << 'cycle_member'
-      }
-      if(verbose) println ""
-      // we can not continue searching after the loop so ,we pop back to treat
-      // the first node in the loop as though a leaf node.
-      path_stack = path_stack[0..cycle_start_index]
-      return ret_value
-    }
-
-    // a 'de-facto' leaf node test subtleties here because we have not yet
-    // determined if the nodes we are wellformed. This is purposeful ,as
-    // this function does not know about the relationships between the 
-    // possible error marks.
-    def local_node = lookup(local_node_label)
-    if(local_node.neighbor.isEmpty()){
-      ret_value << 'defacto_leaf_node'
-      return ret_value
-    }
-
-    // Mark the wellformed nodes and get the result
-    def result = mark_the_wellformed_f(local_node.neighbor ,verbose)
-    if(result == 'exists_malformed'){
-      ret_value << 'exists_malformed'
-    }
-    
-    // Descend further into the tree.
-    path_stack << local_node.neighbor.clone()
-    local_node_label = local_node.neighbor[0]
-    local_path << local_node_label
-  }while(true)
-}
-
-
-
-/*--------------------------------------------------------------------------------
- This function defines the graph.
-
- Lookup attempts to lookup a node label in the node_map, and failing that, it
- tries each label pattern recognition function in order.
-
- lookup_marked_good can be run after `wellformed_graph_f` has marked up the
- graph.  It will only return a node if a) found b) that is 'good'.
- Note the `marked_good_q` adjective above.
-
-*/
-def lookup(node_label ,verbose = false){
-  def lookup_node = node_map[node_label]
-  if( lookup_node ){
-    lookup_node.label = node_label
-  } else {
-    def match_result
-    for(func in node_f_list){
-      match_result = func(node_label)
-      if(match_result.status == "matched"){
-        lookup_node = match_result
-        break
-      }
-    }
-  }
-  if( !lookup_node ){
-    if( verbose ) println "lookup:: Node ${node_label} could not be found."
-    return null
-  }
-  return lookup_node
-}
-
-// mark aware lookup function
-def lookup_marked_good(node_label ,verbose = false){
-  def node = lookup(node_label ,verbose)
-  if( node && marked_good_q(node) ) return node;
-  return null;
-}
-
-
-/*
- Given `root_node_labels` of a DAG. Applies `node_function` to each node in a
- depth-first traversal order.  Returns a set of error tokens encountered
- during traversal.
-
- `wellformed_graph_q` must be run on the DAG before this function is called ,or
- `lookup_marked_good` will not function correctly.
-*/
-def all_DAG_DF(root_node_labels ,node_function ,boolean verbose = true) {
-  def error_token_set = [] as Set
-
-  if (root_node_labels.isEmpty()) return error_token_set
-
-  def visited = [] as Set
-  def in_traversal_order = []
-  def stack = []
-
-  root_node_labels.each { root_label ->
-    stack << root_label
-  }
-
-  do {
-    def node_label = stack.pop()
-    
-    def node = lookup_marked_good(node_label ,verbose)
-    if (!node) {
-      error_token_set << 'lookup_fail'
-      continue
-    }
-
-    if (node.label in visited) continue
-    visited << node.label
-
-    in_traversal_order << node
-
-    node.neighbor.each { neighbor_label ->
-      stack << neighbor_label
-    }
-  } while (!stack.isEmpty())
-
-  in_traversal_order.reverse().each { node ->
-    node_function(node ,error_token_set ,verbose)
-  }
-
-  return error_token_set
-}
-
-
-/*--------------------------------------------------------------------------------
- run the build scripts
-   depends upon is_acyclic having already marked up the graph.
-*/
-
-import java.nio.file.Files
-import java.nio.file.Paths
-
-// a symbol dependency is good ,as long as it is built before the node in question
-def good_dependency_q(node_labels) {
-  return node_labels.every { node_label ->
-    def node = lookup_marked_good(node_label)
-    if (!node) return false
-    if (node.type in ['path' ,'leaf'] && !file_exists_q(node.label)) return false
-    return true
-  }
-}
-
-/* 
- Given a node label and a list of node labels ,returns true if the file at the
- node label in the first argument is newer than all the files at the
- corresponding node labels in the second list.
-*/
-def newer_than_all(node_label ,node_label_list) {
-  def node_path = Paths.get(node_label)
-  if (!Files.exists(node_path)) return false
-
-  def node_last_modified = Files.getLastModifiedTime(node_path).toMillis()
-
-  return node_label_list.every { label ->
-    def path = Paths.get(label)
-    if (!Files.exists(path)) return false
-    def last_modified = Files.getLastModifiedTime(path).toMillis()
-    return node_last_modified > last_modified
-  }
-}
-
-def can_be_built_q(node){
-  if( !marked_good_q(node) ) return false;
-  if( 
-    (node.type == 'symbol' || type == 'path')
-    && !good_dependency_q( node.neighbor )
-  ){
-    return false
-  }
-  if(
-    node.type == 'leaf'
-    && !file_exists_q(node.label)
-  ){ 
-    return false;
-  }
-  return true
-}
-
-// `can_be_build_q` must be true for this to be meaningful:
-def should_be_built_q(node ,verbose = true) {
-  if(node.type == 'leaf') return false
-  if(node.type == 'symbol') return true
-  if( node.type == 'path') return !newer_than_all(node.label ,node.neighbor)
-  println("should_be_build_q:: unrecognized node type ,so assuming it should not be built.")
-  return false
-}
-
-/*
- Runs build scripts for the given root_node_labels in a depth-first traversal order.
- Uses `all_DAG_DF` to traverse the graph and applies the build function to each node.
-
- Be sure that `wellformed_graph_q` has been run on DAG.
-
- Be sure that the 'build_failed' marks have been cleared if this is not the
- first build attempt.
-*/
-def run_build_scripts_f(root_node_labels ,boolean verbose = true){
-  if( root_node_labels.isEmpty() ) return
-
-  // Define the function to be applied to each node
-  def node_function = { node ,error_token_set ->
-
-    if( !can_be_built_q(node) ){
-      println("Skipping build for ${node.label} due to dependency problems or found leaf is missing")
-      return
-    }
-
-    if( !should_be_built_q(node) ){
-      if(verbose) println("${node.label} already up to date")
-      return
-    }
-
-    // if we get here, node can and should be built
-
-    println("Running build script for ${node.label}")
-    node.build(node ,node.neighbor)
-
-    // Check if the build updated the target file
-    if( should_be_built_q(node) ){
-      println("Build failed for ${node.label}")
-      set_mark(node ,'build_failed')
-    }
-
-  }
-
-  println("run_build_scripts_f:: running ...")
-  // Apply the function to all nodes in a depth-first manner
-  all_DAG_DF(root_node_labels ,node_function ,verbose)
-}
-
-
-// LocalWords:  FN FPL DN DNL RuleNameListRegx RuleNameList PrintVisitorMethod
-// LocalWords:  PrintVisitor SyntaxAnnotate wellformed defacto acyclic
-// LocalWords:  wellformed unvisited
diff --git a/developer/deprecated/Ariandne2.groovy b/developer/deprecated/Ariandne2.groovy
new file mode 100644 (file)
index 0000000..6c0cf17
--- /dev/null
@@ -0,0 +1,635 @@
+class AriadneGraph {
+
+  // Instance variables for graph data if needed
+  Map node_map = [:]
+  List node_f_list = []
+
+  // Constructor to accept a graph definition (node_map and node_f_list)
+  AriadneGraph( Map node_map ,List node_f_list ){
+    this.node_map = node_map ?: [:]
+    this.node_f_list = node_f_list ?: []
+  }
+
+  /*--------------------------------------------------------------------------------
+   File utility functions
+  */
+
+  static Map unpack_file_path( String file_fp ){
+    def file = new File( file_fp )
+
+    def parent_dp = file.getParent()
+    def file_fn = file.getName()
+    def file_fn_base = file_fn.lastIndexOf('.') > 0 ? file_fn[ 0..file_fn.lastIndexOf('.') - 1 ] : file_fn
+    def file_fn_ext = file_fn.lastIndexOf('.') > 0 ? file_fn[ file_fn.lastIndexOf('.') + 1..-1 ] : ''
+
+    return [
+      parent_dp: parent_dp
+      ,file_fn: file_fn
+      ,file_fn_base: file_fn_base
+      ,file_fn_ext: file_fn_ext
+    ]
+  }
+
+  static boolean file_exists_q( String node_label ){
+    def node_path = Paths.get( node_label )
+    return Files.exists( node_path )
+  }
+
+  /*--------------------------------------------------------------------------------
+   Node type checks and marking
+  */
+
+  static Set all_node_type_set = ['symbol' ,'path' ,'leaf' ,'generator'] as Set
+  static Set persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as Set
+
+  static boolean leaf_q( Map node ){
+    return node && node.type == 'leaf'
+  }
+
+  static boolean has_mark( Map node ){
+    return node?.mark?.isNotEmpty()
+  }
+
+  static void set_mark( Map node ,String mark ){
+    node.mark = node.mark ?: [] as Set
+    node.mark << mark
+  }
+
+  static void clear_mark( Map node ,String mark ){
+    node?.mark?.remove( mark )
+  }
+
+  static boolean marked_good_q( Map node ){
+    return node && node.mark && ( 'wellformed' in node.mark ) && !( 'cycle_member' in node.mark ) && !( 'build_failed' in node.mark )
+  }
+
+  /*--------------------------------------------------------------------------------
+   Well-formed Node Check
+  */
+
+  static Set all_form_error_set = [
+    'no_node'
+    ,'node_must_have_label'
+    ,'label_must_be_string'
+    ,'node_must_have_type'
+    ,'bad_node_type'
+    ,'neighbor_value_must_be_list'
+    ,'neighbor_reference_must_be_string'
+    ,'mark_property_value_must_be_set'
+    ,'unregistered_mark'
+    ,'missing_required_build_code'
+    ,'leaf_given_neighbor_property'
+    ,'leaf_given_build_property'
+  ] as Set
+
+  static Set wellformed_q( Map node ){
+    def form_error_set = [] as Set
+
+    if( !node ){
+      form_error_set << 'no_node'
+      return form_error_set
+    }
+
+    if( !node.label )
+      form_error_set << 'node_must_have_label'
+    else if( !( node.label instanceof String ) )
+      form_error_set << 'label_must_be_string'
+
+    if( !node.type )
+      form_error_set << 'node_must_have_type'
+    else if( !( node.type instanceof String ) || !( node.type in all_node_type_set ) )
+      form_error_set << 'bad_node_type'
+
+    if( node.neighbor ){
+      if( !( node.neighbor instanceof List ) )
+        form_error_set << 'neighbor_value_must_be_list'
+      else if( !( node.neighbor.every { it instanceof String } ) )
+        form_error_set << 'neighbor_reference_must_be_string'
+    }
+
+    if( node.mark ){
+      if( !( node.mark instanceof Set ) )
+        form_error_set << 'mark_property_value_must_be_set'
+      else if( !( node.mark.every { it in persistent_node_mark_set } ) )
+        form_error_set << 'unregistered_mark'
+    }
+
+    if( node.type == 'path' && ( !node.build || !( node.build instanceof Closure ) ) )
+      form_error_set << 'missing_required_build_code'
+
+    if( node.type == 'leaf' ){
+      if( node.neighbor ) form_error_set << 'leaf_given_neighbor_property'
+      if( node.build ) form_error_set << 'leaf_given_build_property'
+    }
+
+    return form_error_set
+  }
+
+  /*--------------------------------------------------------------------------------
+   Graph traversal and build functions
+  */
+
+  def lookup( String node_label ,boolean verbose = false ){
+    def lookup_node = node_map[ node_label ]
+    if( !lookup_node ){
+      def match_result
+      for( func in node_f_list ){
+        match_result = func( node_label )
+        if( match_result.status == "matched" ){
+          lookup_node = match_result
+          break
+        }
+      }
+    }
+
+    if( !lookup_node && verbose ) println "lookup:: Node ${node_label} could not be found."
+    return lookup_node
+  }
+
+  def run_build_scripts_f( List root_node_labels ,boolean verbose = true ){
+    if( root_node_labels.isEmpty() ) return
+
+    def node_function = { node ,error_token_set ->
+
+      if( !can_be_built_q( node ) ){
+        println( "Skipping build for ${node.label} due to dependency problems" )
+        return
+      }
+      if( !should_be_built_q( node ) ){
+        if( verbose ) println( "${node.label} already up to date" )
+        return
+      }
+
+      println( "Running build script for ${node.label}" )
+      node.build( node ,node.neighbor )
+
+      if( should_be_built_q( node ) ){
+        println( "Build failed for ${node.label}" )
+        set_mark( node ,'build_failed' )
+      }
+    }
+
+    println( "run_build_scripts_f:: running ..." )
+    all_DAG_DF( root_node_labels ,node_function ,verbose )
+  }
+
+  // Add the rest of your methods here as instance/static methods based on whether they depend on the graph instance
+
+}
+/*
+ Some terms:
+
+ a node is either malformed or 'wellformed'.  A wellformed node meets
+ the criteria set forth by `well_formed_node_q`.
+
+ 'node': a dictionary, hopefully a wellformed one
+ 'file node': a 'path' or 'leaf' type node.
+ 'node file': for 'path' or 'leaf' nodes, path relative to the developer's directory.
+
+ This code makes use of the following node properties:
+ - label: Unique label string. Used to reference nodes.
+ - type: The type of the node: 'leaf' ,'symbol' ,or 'path'.
+ - neighbor: list of the neighbor nodes labels
+ - must_have: list of node labels
+ - build: Code that builds the node.
+ - mark: a set of 'mark' tokens optionally placed on a node
+
+ For 'path' and 'leaf' type nodes, the node label is a file path.  Relative paths
+ are relative to the developer's directory.
+
+ The dependency graph is defined by the `lookup` function. `lookup` when given
+ a node label returns the node, if found, or null.
+
+*/
+
+/*--------------------------------------------------------------------------------
+ file utility functions
+*/
+
+def unpack_file_path = { file_fp ->
+  def file = new File(file_fp)
+
+  // Get the parent directory path
+  def parent_dp = file_fn.getParent()
+
+  // Get the file name (with extension)
+  def file_fn = file_fn.getName()
+
+  // Split the file name into base and extension
+  def file_fn_base = file_fn.lastIndexOf('.') > 0 ? file_fn[0..file_fn.lastIndexOf('.') - 1] : file_fn
+  def file_fn_ext = file_fn.lastIndexOf('.') > 0 ? file_fn[file_fn.lastIndexOf('.') + 1..-1] : ''
+
+  // Return the components as a map for easier use
+  return [
+    parent_dp: parent_dp
+    ,file_fn: file_fn
+    ,file_fn_base: file_fn_base
+    ,file_fn_ext: file_fn_ext
+  ]
+}
+
+def file_exists_q(node_label) {
+  def node_path = Paths.get(node_label)
+  return Files.exists(node_path)
+}
+
+
+/*-------------------------------------------------------------------------------
+  DAG adjectives
+
+  node undefined for undefined node?
+*/
+
+// A node is type 'leaf' when the node file can not be deleted or replaced.
+// This type used to be called 'primary', which might be a better term.
+
+def all_node_type_set = ['symbol' ,'path' ,'leaf' ,'generator'] as Set
+
+def persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as Set
+def leaf_q = { node -> node && node.type && node.type == 'leaf' }
+
+// mark 
+def has_mark = { node -> node.mark && !node.mark.isEmpty() }
+def set_mark(node ,mark){
+  node.mark = node.mark ?: [] as Set
+  node.mark << mark
+}
+def clear_mark(node ,mark){
+  if( node.mark ) node.mark.remove(mark)
+}
+def marked_good_q(node){
+  return (
+    node 
+    && node.mark 
+    && ('wellformed' in node.mark) 
+    && !('cycle_member' in node.mark)
+    && !('build_failed' in node.mark)
+  )
+}
+
+
+
+/*--------------------------------------------------------------------------------
+ Wellformed Node Check
+
+ The developer defines the nodes, so their form needs to be checked.
+
+ early bail on node not defined
+ legal path in the build directory makedir -p ... not really a acquirement 
+
+*/
+def all_form_error_set = [
+  'no_node'
+  ,'node_must_have_label'
+  ,'label_must_be_string'
+  ,'node_must_have_type'
+  ,'bad_node_type'
+  ,'neighbor_value_must_be_list'
+  ,'neighbor_reference_must_be_string'
+  ,'mark_property_value_must_be_set'
+  ,'unregistered_mark'
+  ,'missing_required_build_code'
+  ,'leaf_given_neighbor_property'
+  ,'leaf_given_build_property'
+]
+def wellformed_q = { node -> 
+  def form_error_set = [] as Set
+
+  if( !node ){
+    form_error_set << 'no_node'
+    return form_error_set
+  }
+
+  if( !node.label )
+    form_error_set << 'node_must_have_label'
+  else if( !(node.label instanceof String) )
+    form_error_set << 'label_must_be_string'
+
+  if( !node.type )
+    form_error_set << 'node_must_have_type'
+  else if( !(node.type instanceof String) || !(node.type in all_node_type_set) )
+    form_error_set << 'bad_node_type'
+    
+  if( node.neighbor ){
+    if( !(node.neighbor instanceof List) )
+      form_error_set << 'neighbor_value_must_be_list'
+    else if( !(node.neighbor.every { it instanceof String }) )
+      form_error_set << 'neighbor_reference_must_be_string'
+  }
+
+  if( node.mark ){
+    if( !(node.mark instanceof Set) ) 
+      form_error_set << 'mark_property_value_must_be_set'
+    else if( !(node.mark.every { it in persistent_node_mark_set }) )
+      form_error_set << 'unregistered_mark'
+  }
+
+  // Removed test about symbol build needing neighbors
+
+  if( 
+    node.type == "path" 
+    && (!node.build || !(node.build instanceof Closure))
+  )
+    form_error_set << 'missing_required_build_code'
+
+  // Removed test about path needing neighbors
+
+  // Can a primary file have neighbors or a build script? Our build model
+  // has the node file as a target, the build script as a means of building
+  // the target (hence its name), and the dependencies as inputs to the build
+  // script. So in the spirit of this model, we will say "no".
+  if( node.type == "leaf" ){
+    if( node.neighbor ) form_error_set << 'leaf_given_neighbor_property'
+    if( node.build ) form_error_set << 'leaf_given_build_property'
+  }
+
+  return form_error_set
+}
+
+
+
+/*--------------------------------------------------------------------------------
+ A well formed graph checker.  Traverses entire graph and marks nodes
+ that are not well formed or that are part of a cycle.
+
+*/
+
+// given a node label list, adds the 'wellformed' mark to wellformed nodes.
+def mark_the_wellformed_f(node_label_list ,boolean verbose = true){
+  def all_wellformed = true
+
+  def neighbors = node_label_list.collect{ neighbor_label ->
+    def neighbor_node = lookup(neighbor_label)
+    def form_errors = wellformed_q(neighbor_node)
+    if(form_errors.isEmpty()){
+      neighbor_node.mark = neighbor_node.mark ?: [] as Set
+      neighbor_node.mark << 'wellformed'
+    } else {
+      all_wellformed = false
+      if(verbose){
+        if(neighbor_node.label && neighbor_node.label.length() > 0){
+          print("node ${neighbor_node.label} is malformed due to:")
+        } else {
+          print("anonymous node is malformed due to:")
+        }
+        form_errors.each { error -> print(" ${error}") }
+        println("")
+      }
+    }
+    neighbor_label
+  }
+
+  return all_wellformed ? 'all_wellformed' : 'exists_malformed'
+}
+
+// descends un-visited leftmost path, while marking nodes as wellformed and if
+// they are part of a cycle.
+def markup_graph_f_descend(path_stack ,boolean verbose = true){
+  def ret_value = [] as Set
+  def local_path = path_stack.collect{ it[0] }
+  def local_node_label = local_path[-1]
+  def cycle_start_index
+
+  do{
+    // Check for a cycle in the local path
+    cycle_start_index = local_path[0..-2].findIndexOf{ it == local_node_label }
+    if(cycle_start_index != -1){ // Cycle detected
+      ret_value << 'cycle_found'
+      if(verbose) print "markup_graph_f_descend:: dependency cycle found:"
+      local_path[cycle_start_index..-1].each{ cycle_node_label ->
+        def cycle_node = lookup(cycle_node_label)
+        if(verbose) print " ${cycle_node.label}"
+        cycle_node.mark = cycle_node.mark ?: [] as Set // Initialize mark set if needed
+        cycle_node.mark << 'cycle_member'
+      }
+      if(verbose) println ""
+      // we can not continue searching after the loop so ,we pop back to treat
+      // the first node in the loop as though a leaf node.
+      path_stack = path_stack[0..cycle_start_index]
+      return ret_value
+    }
+
+    // a 'de-facto' leaf node test subtleties here because we have not yet
+    // determined if the nodes we are wellformed. This is purposeful ,as
+    // this function does not know about the relationships between the 
+    // possible error marks.
+    def local_node = lookup(local_node_label)
+    if(local_node.neighbor.isEmpty()){
+      ret_value << 'defacto_leaf_node'
+      return ret_value
+    }
+
+    // Mark the wellformed nodes and get the result
+    def result = mark_the_wellformed_f(local_node.neighbor ,verbose)
+    if(result == 'exists_malformed'){
+      ret_value << 'exists_malformed'
+    }
+    
+    // Descend further into the tree.
+    path_stack << local_node.neighbor.clone()
+    local_node_label = local_node.neighbor[0]
+    local_path << local_node_label
+  }while(true)
+}
+
+
+
+/*--------------------------------------------------------------------------------
+ This function defines the graph.
+
+ Lookup attempts to lookup a node label in the node_map, and failing that, it
+ tries each label pattern recognition function in order.
+
+ lookup_marked_good can be run after `wellformed_graph_f` has marked up the
+ graph.  It will only return a node if a) found b) that is 'good'.
+ Note the `marked_good_q` adjective above.
+
+*/
+def lookup(node_label ,verbose = false){
+  def lookup_node = node_map[node_label]
+  if( lookup_node ){
+    lookup_node.label = node_label
+  } else {
+    def match_result
+    for(func in node_f_list){
+      match_result = func(node_label)
+      if(match_result.status == "matched"){
+        lookup_node = match_result
+        break
+      }
+    }
+  }
+  if( !lookup_node ){
+    if( verbose ) println "lookup:: Node ${node_label} could not be found."
+    return null
+  }
+  return lookup_node
+}
+
+// mark aware lookup function
+def lookup_marked_good(node_label ,verbose = false){
+  def node = lookup(node_label ,verbose)
+  if( node && marked_good_q(node) ) return node;
+  return null;
+}
+
+
+/*
+ Given `root_node_labels` of a DAG. Applies `node_function` to each node in a
+ depth-first traversal order.  Returns a set of error tokens encountered
+ during traversal.
+
+ `wellformed_graph_q` must be run on the DAG before this function is called ,or
+ `lookup_marked_good` will not function correctly.
+*/
+def all_DAG_DF(root_node_labels ,node_function ,boolean verbose = true) {
+  def error_token_set = [] as Set
+
+  if (root_node_labels.isEmpty()) return error_token_set
+
+  def visited = [] as Set
+  def in_traversal_order = []
+  def stack = []
+
+  root_node_labels.each { root_label ->
+    stack << root_label
+  }
+
+  do {
+    def node_label = stack.pop()
+    
+    def node = lookup_marked_good(node_label ,verbose)
+    if (!node) {
+      error_token_set << 'lookup_fail'
+      continue
+    }
+
+    if (node.label in visited) continue
+    visited << node.label
+
+    in_traversal_order << node
+
+    node.neighbor.each { neighbor_label ->
+      stack << neighbor_label
+    }
+  } while (!stack.isEmpty())
+
+  in_traversal_order.reverse().each { node ->
+    node_function(node ,error_token_set ,verbose)
+  }
+
+  return error_token_set
+}
+
+
+/*--------------------------------------------------------------------------------
+ run the build scripts
+   depends upon is_acyclic having already marked up the graph.
+*/
+
+import java.nio.file.Files
+import java.nio.file.Paths
+
+// a symbol dependency is good ,as long as it is built before the node in question
+def good_dependency_q(node_labels) {
+  return node_labels.every { node_label ->
+    def node = lookup_marked_good(node_label)
+    if (!node) return false
+    if (node.type in ['path' ,'leaf'] && !file_exists_q(node.label)) return false
+    return true
+  }
+}
+
+/* 
+ Given a node label and a list of node labels ,returns true if the file at the
+ node label in the first argument is newer than all the files at the
+ corresponding node labels in the second list.
+*/
+def newer_than_all(node_label ,node_label_list) {
+  def node_path = Paths.get(node_label)
+  if (!Files.exists(node_path)) return false
+
+  def node_last_modified = Files.getLastModifiedTime(node_path).toMillis()
+
+  return node_label_list.every { label ->
+    def path = Paths.get(label)
+    if (!Files.exists(path)) return false
+    def last_modified = Files.getLastModifiedTime(path).toMillis()
+    return node_last_modified > last_modified
+  }
+}
+
+def can_be_built_q(node){
+  if( !marked_good_q(node) ) return false;
+  if( 
+    (node.type == 'symbol' || type == 'path')
+    && !good_dependency_q( node.neighbor )
+  ){
+    return false
+  }
+  if(
+    node.type == 'leaf'
+    && !file_exists_q(node.label)
+  ){ 
+    return false;
+  }
+  return true
+}
+
+// `can_be_build_q` must be true for this to be meaningful:
+def should_be_built_q(node ,verbose = true) {
+  if(node.type == 'leaf') return false
+  if(node.type == 'symbol') return true
+  if( node.type == 'path') return !newer_than_all(node.label ,node.neighbor)
+  println("should_be_build_q:: unrecognized node type ,so assuming it should not be built.")
+  return false
+}
+
+/*
+ Runs build scripts for the given root_node_labels in a depth-first traversal order.
+ Uses `all_DAG_DF` to traverse the graph and applies the build function to each node.
+
+ Be sure that `wellformed_graph_q` has been run on DAG.
+
+ Be sure that the 'build_failed' marks have been cleared if this is not the
+ first build attempt.
+*/
+def run_build_scripts_f(root_node_labels ,boolean verbose = true){
+  if( root_node_labels.isEmpty() ) return
+
+  // Define the function to be applied to each node
+  def node_function = { node ,error_token_set ->
+
+    if( !can_be_built_q(node) ){
+      println("Skipping build for ${node.label} due to dependency problems or found leaf is missing")
+      return
+    }
+
+    if( !should_be_built_q(node) ){
+      if(verbose) println("${node.label} already up to date")
+      return
+    }
+
+    // if we get here, node can and should be built
+
+    println("Running build script for ${node.label}")
+    node.build(node ,node.neighbor)
+
+    // Check if the build updated the target file
+    if( should_be_built_q(node) ){
+      println("Build failed for ${node.label}")
+      set_mark(node ,'build_failed')
+    }
+
+  }
+
+  println("run_build_scripts_f:: running ...")
+  // Apply the function to all nodes in a depth-first manner
+  all_DAG_DF(root_node_labels ,node_function ,verbose)
+}
+
+
+// LocalWords:  FN FPL DN DNL RuleNameListRegx RuleNameList PrintVisitorMethod
+// LocalWords:  PrintVisitor SyntaxAnnotate wellformed defacto acyclic
+// LocalWords:  wellformed unvisited
diff --git a/developer/deprecated/Graph.java b/developer/deprecated/Graph.java
new file mode 100644 (file)
index 0000000..3052338
--- /dev/null
@@ -0,0 +1,685 @@
+package Ariadne;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+
+public class Graph {
+
+  /*--------------------------------------------------------------------------------
+   type aliases
+  */
+  public class TokenSet extends HashSet<Token>{}
+  public class LabelList extends ArrayList<Label> {}
+  public interface Node extends Map<Label ,Object>{}
+  public interface NodeList extends List<Node>{}
+  public interface RecognizerF extends Function<Label ,Node>{}
+  public class RecognizerFList extends ArrayList<RecognizerF> {}
+
+  /*--------------------------------------------------------------------------------
+   instance data 
+  */
+
+  private static boolean debug = true;
+  private Map<Label ,Node> node_map;
+  private RecognizerFList recognizer_f_list;
+
+  /*--------------------------------------------------------------------------------
+    constructors
+  */
+
+  public AriadneGraph(Map<Label ,Node> node_map ,RecognizerFList recognizer_f_list){
+    if (node_map == null && recognizer_f_list == null) {
+      System.err.println("AriadneGraph: At least one of 'node_map' (Map) or 'recognizer_f_list' (List) must be provided.");
+      System.exit(1);
+    }
+
+    // Initialize node_map and recognizer_f_list to empty collections if they are null
+    this.node_map = (node_map != null) ? node_map : new HashMap<Label ,Node>();
+
+    this.recognizer_f_list = (recognizer_f_list != null) ? recognizer_f_list : new RecognizerFList();
+  }
+
+  /*--------------------------------------------------------------------------------
+   file utilities
+  */
+
+  public static Map<String ,String> unpack_file_path(String file_fp){
+    if(debug) System.out.println("unpack_file_path::file_fp: " + file_fp);
+
+    File file = new File(file_fp);
+    String parent_dp = (file.getParent() != null) ? file.getParent() : "";
+
+    if( !parent_dp.isEmpty() && !parent_dp.endsWith(File.separator) ){
+      parent_dp += File.separator;
+    }
+
+    String file_fn = file.getName();
+    String file_fn_base = file_fn;
+    String file_fn_ext = "";
+
+    int last_index = file_fn.lastIndexOf('.');
+    if(last_index > 0){
+      file_fn_base = file_fn.substring(0 ,last_index);
+      if( last_index + 1 < file_fn.length() ){
+        file_fn_ext = file_fn.substring(last_index + 1);
+      }
+    }
+
+    Map<String ,String> ret_val = new HashMap<>();
+    ret_val.put("dp" ,parent_dp);
+    ret_val.put("fn" ,file_fn);
+    ret_val.put("fn_base" ,file_fn_base);
+    ret_val.put("fn_ext" ,file_fn_ext);
+
+    if(debug) System.out.println("unpack_file_path::ret_val: " + ret_val);
+
+    return ret_val;
+  }
+
+  public static boolean file_exists_q(Label node_label){
+    Path node_path = Paths.get(node_label);
+    return Files.exists(node_path);
+  }
+
+  /*--------------------------------------------------------------------------------
+    About nodes
+
+    A leaf type node specifies a path to a file that should not be deleted by
+    in clean operations. Typically this is the source code. We could add a
+    tool to lock permissions on these before a build, so that the build
+    scripts will also not mess with them (unless they change permissions).
+
+    If the user has multiple dependency graphs defined, a node with no
+    dependencies in one graph, might have dependencies in another.
+
+    An error type node, is one that was found to not have a type, or
+    was constructed by the tool to be a place older, perhaps for a
+    node label that was not found.
+
+  */
+  public static TokenSet all_node_type_set = new TokenSet();
+  static {
+    all_node_type_set.add(new Token("symbol"));
+    all_node_type_set.add(new Token("path"));
+    all_node_type_set.add(new Token("leaf"));
+    all_node_type_set.add(new Token("error"));
+  }
+
+  public static TokenSet persistent_node_mark_set = new TokenSet();
+  static {
+    persistent_node_mark_set.add(new Token("cycle_member"));
+    persistent_node_mark_set.add(new Token("wellformed"));
+    persistent_node_mark_set.add(new Token("build_failed"));
+    persistent_node_mark_set.add(new Token("null_node"));
+  }
+
+  public static boolean leaf_q(Node node){
+    return node != null && "leaf".equals(node.get("type"));
+  }
+
+  public static boolean has_mark(Node node){
+    return node != null && node.get("mark") != null && !( (TokenSet)node.get("mark") ).isEmpty();
+  }
+
+  public static void set_mark(Node node ,Token mark){
+    if( node.get("mark") == null ){
+      node.put("mark" ,new HashTokenSet());
+    }
+    ( (TokenSet)node.get("mark") ).add(mark);
+  }
+
+  public static void clear_mark(Node node ,Token mark){
+    if( node != null && node.get("mark") != null ){
+      ( (TokenSet) node.get("mark") ).remove(mark);
+    }
+  }
+
+  public static boolean marked_good_q(Node node){
+    return node != null && node.get("mark") != null
+      && ( (TokenSet)node.get("mark") ).contains("wellformed")
+      && !( (TokenSet)node.get("mark") ).contains("cycle_member")
+      && !( (TokenSet)node.get("mark") ).contains("build_failed");
+  }
+
+
+  /*--------------------------------------------------------------------------------
+   Well-formed Node Check
+  */
+
+  public static TokenSet form_condition_set = new TokenSet();
+  static {
+    form_condition_set.add(new Token("no_node"));
+    form_condition_set.add(new Token("node_must_have_label"));
+    form_condition_set.add(new Token("label_must_be_string"));
+    form_condition_set.add(new Token("node_must_have_type"));
+    form_condition_set.add(new Token("bad_node_type"));
+    form_condition_set.add(new Token("neighbor_value_must_be_list"));
+    form_condition_set.add(new Token("neighbor_reference_must_be_string"));
+    form_condition_set.add(new Token("neighbor_label_not_in_graph"));
+    form_condition_set.add(new Token("mark_property_value_must_be_set"));
+    form_condition_set.add(new Token("unregistered_mark"));
+    form_condition_set.add(new Token("missing_required_build_code"));
+    form_condition_set.add(new Token("leaf_given_neighbor_property"));
+    form_condition_set.add(new Token("leaf_given_build_property"));
+  }
+
+  // given a node, collects a description of its form, returns a set form condition tokens
+  public static TokenSet wellformed_node_q(Node node){
+    TokenSet form_error_set = new HashSet<>();
+
+    if(node == null){
+      form_error_set.add("null_node");
+      return form_error_set;
+    }
+
+    if( !node.containsKey("label") )
+      form_error_set.add("node_must_have_label");
+    else if( !(node.get("label") instanceof Label) )
+      form_error_set.add("label_must_be_string");
+
+    if( !node.containsKey("type") )
+      form_error_set.add("node_must_have_type");
+    else if( !(node.get("type") instanceof String) || !all_node_type_set.contains(node.get("type")) )
+      form_error_set.add("bad_node_type");
+
+    if( node.containsKey("neighbor") ){
+      if( !(node.get("neighbor") instanceof List) )
+        form_error_set.add("neighbor_value_must_be_list");
+      else if( !((List<?>) node.get("neighbor")).stream().allMatch(it -> it instanceof Label) )
+        form_error_set.add("neighbor_reference_must_be_string");
+    }
+
+    if( node.containsKey("mark") ){
+      if( !(node.get("mark") instanceof Set) )
+        form_error_set.add("mark_property_value_must_be_set");
+      else if( !((Set<?>) node.get("mark")).stream().allMatch(it -> persistent_node_mark_set.contains(it)) )
+        form_error_set.add("unregistered_mark");
+    }
+
+    if( "path".equals(node.get("type")) && (!node.containsKey("build") || !(node.get("build") instanceof Runnable)) )
+      form_error_set.add("missing_required_build_code");
+
+    if( "leaf".equals(node.get("type")) ){
+      if( node.containsKey("neighbor") ) form_error_set.add("leaf_given_neighbor_property");
+      if( node.containsKey("build") ) form_error_set.add("leaf_given_build_property");
+    }
+
+    return form_error_set;
+  }
+
+  // given a node, potentially marks it as wellformed, returns one of 'wellformed' or 'malformed'
+  public static Token wellformed_mark_node(Node node ,boolean verbose){
+    if(debug){
+      if(node != null){
+        System.out.println("wellformed_mark_node::node: " + node);
+      }else{
+        System.out.println("wellformed_mark_node given a null node");
+      }
+    }
+
+    TokenSet form_errors = wellformed_node_q(node);
+    if( form_errors.isEmpty() ){
+      set_mark( node ,"wellformed" );
+      return "wellformed";
+    }
+
+    // At this point we know that form_errors is not empty
+    if(verbose){
+      if( node != null && node.get("label") != null && ((Label)node.get("label")).length() > 0 ){
+        System.out.print( "node " + node.get("label") + " is malformed due to:" );
+      }else{
+        System.out.print("anonymous node is malformed due to:");
+      }
+      for(Token error : form_errors){
+        System.out.print(" " + error);
+      }
+      System.out.println("");
+    }
+
+    return "malformed";
+  }
+  public Token wellformed_mark_node(Node node){
+    return wellformed_mark_node(node ,true);
+  }
+
+  // given a node_label, potentially marks the corresponding node as 'wellformed', returns a token set.
+  // Tokens included "undefined_node", "malformed", and "defactor_leaf".
+  public TokenSet wellformed_mark_node_label(Label node_label ,boolean verbose){
+    TokenSet ret_value = new HashSet<>();
+    Node node = lookup(node_label);
+    if(node == null){
+      ret_value.add("undefined_node");
+      return ret_value;
+    }
+    if( "malformed".equals(wellformed_mark_node(node ,verbose)) ){
+      ret_value.add("malformed");
+    }
+    if( ((List<?>)node.get("neighbor")).isEmpty() ){
+      ret_value.add("defacto_leaf"); // might not be `type:leaf`
+    }
+    return ret_value;
+  }
+  public TokenSet wellformed_mark_node_label(Label node_label){
+    return wellformed_mark_node_label(node_label ,true);
+  }
+
+
+  /*--------------------------------------------------------------------------------
+   A well formed graph checker.  Traverses entire graph and marks nodes
+   that are not well formed or that are part of a cycle.
+
+   This must be run on the graph for `lookup_marked_good` to work.
+
+   Each node_label must be a string and not empty.
+
+   Subleties here because we have not yet determined if the nodes we are
+   wellformed (after all ,that is what we are determining here).
+
+   If we want to attempt to build 'islands' of things that might be located on
+   the far side of cycles ,then modify the cycle finder to return a list of
+   cycles (i.e. a list of lists) ,then use each of cycle definition (a list) as
+   the root nodes for further search.
+
+   `path_stack` is a stack of LabelList. The first entry is a clone of the list of
+   root nodes, referenced by label. Each subsequent list is a clone of the
+   neighbor list of the leftmost node of the prior entry.
+
+   `path` is a list of the left most nodes, referenced by label, of the entries
+   on the path stack. This is the path to our current location in the tree.
+  */
+
+
+  private boolean find_and_remove_cycle(List<LabelList> path_stack ,LabelList path ,boolean verbose){
+
+    if( path.size() <= 1 ) return false; // 0 or 1 length path can't have a cycle
+
+    // we want to know if the most recent node added to the path occurs at a point earlier
+    // in the path.
+    int rightmost_index = path.size() - 1;
+    Label recent_node_label = path.get( rightmost_index );
+    int cycle_start_index = path.indexOf(recent_node_label);
+    if( cycle_start_index == -1 ){
+      System.err.println("find_and_remove_cycle:: indexOf does not find index of known list member");
+      return false;
+    }
+    Boolean has_cycle =  cycle_start_index < rightmost_index;
+    if(!has_cycle) return false;
+
+    if(verbose) System.out.print("mark_form_graph_descend:: dependency cycle found:");
+    for( Label cycle_node_label : path.subList(cycle_start_index ,path.size()) ){
+      if(verbose) System.out.print(" " + cycle_node_label);
+      Node cycle_node = lookup(cycle_node_label);
+      if( cycle_node.get("mark") == null ){
+        cycle_node.put( "mark" ,new HashTokenSet() );
+      }
+      ( (TokenSet)cycle_node.get("mark") ).add("cycle_member");
+    }
+    if(verbose) System.out.println("");
+
+    // We cannot continue searching after the loop, so we pop back to treat
+    // the first node in the loop as though a leaf node.
+    path_stack.subList( cycle_start_index + 1 ,path_stack.size() ).clear();
+
+    return true;
+  }
+
+  private static TokenSet mark_form_graph_descend_set = new TokenSet();
+  static {
+    mark_form_graph_descend_set.add(new Token("empty_path_stack"));
+    mark_form_graph_descend_set.add(new Token("cycle_found"));
+    mark_form_graph_descend_set.add(new Token("undefined_node"));
+    mark_form_graph_descend_set.add(new Token("exists_malformed"));
+    mark_form_graph_descend_set.add(new Token("defacto_leaf"));
+  }
+
+  private TokenSet mark_form_graph_descend( List<LabelList> path_stack ,boolean verbose ){
+    TokenSet ret_value = new HashSet<>();
+    if(path_stack.isEmpty()){
+      if(verbose) System.out.println( "mark_form_graph_descend:: given empty path_stack to descend from" );
+      ret_value.add( "empty_path_stack" );
+      return ret_value;
+    }
+
+    LabelList local_path = new ArrayList<>();
+    for(LabelList path : path_stack){
+      local_path.add( path.get(0) );
+    }
+    Label local_node_label = local_path.get( local_path.size() - 1 );
+  
+    do{
+      
+      if( find_and_remove_cycle(path_stack ,local_path ,verbose) ){
+        ret_value.add("cycle_found");
+        return ret_value;
+      }
+
+      TokenSet wellformed_mark_node_label_result = wellformed_mark_node_label(local_node_label ,verbose);
+      ret_value.addAll( wellformed_mark_node_label_result );
+      if( 
+         wellformed_mark_node_label_result.contains("undefined_node") 
+         || wellformed_mark_node_label_result.contains("defacto_leaf") 
+      ){
+        return ret_value;
+      }
+
+      // Descend further into the tree.
+      path_stack.add( new ArrayList<>((LabelList) lookup(local_node_label).get("neighbor")) );
+      local_node_label = (LabelList)lookup(local_node_label).get("neighbor").get(0);
+      local_path.add(local_node_label);
+
+    }while(true);
+  }
+
+   
+  /*
+    Given root_node_label_list, marks up the graph and returns a set possibly
+    containing 'all_wellformed' and 'cycles_exist'.
+
+    Marks potentially added to each node include 'cycle_member' and 'wellformed'.
+    Note that these marks are independent.
+  */
+  public TokenSet mark_form_graph(LabelList root_node_label_list ,boolean verbose){
+    TokenSet ret_value = new HashSet<>();
+    boolean exists_malformed = false;
+    TokenSet result; // used variously
+
+    if( root_node_label_list.isEmpty() ) return ret_value;
+
+    // Initialize the DFS tree iterator.
+    List<LabelList> path_stack = new ArrayList<>();
+    path_stack.add( new ArrayList<>(root_node_label_list) );
+
+    // iterate over left side tree descent, not ideal as it starts at the
+    // root each time, but avoids complexity in the cycle detection logic.
+    do{
+      result = mark_form_graph_descend(path_stack ,verbose);
+      if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
+      if( result.contains("undefined_node") ) exists_malformed = true;
+      if( result.contains("exists_malformed") ) exists_malformed = true;
+
+      // increment the iterator to the next leftmost path
+      LabelList top_list = path_stack.get( path_stack.size() - 1 );
+      top_list.remove(0);
+      if( top_list.isEmpty() ) path_stack.remove( path_stack.size() - 1 );
+
+    }while( !path_stack.isEmpty() );
+
+    if( !exists_malformed ) ret_value.add("all_wellformed");
+
+    if( verbose ){
+      if( exists_malformed ) System.out.println("one or more malformed nodes were found");
+      boolean exists_cycle = ret_value.contains("cycle_exists");
+      if( exists_cycle ) System.out.println("one or more cyclic dependency loop found");
+      if( exists_malformed || exists_cycle ) System.out.println("will attempt to build unaffected nodes");
+    }
+
+    return ret_value;
+  }
+  public TokenSet mark_form_graph(LabelList root_node_label_list){
+    return mark_form_graph(root_node_label_list ,true);
+  }
+
+
+  /*--------------------------------------------------------------------------------
+    Graph traversal
+  */
+
+  // Given a node label, looks it up in the dependency graph, returns the node or null
+  public Node lookup(Label node_label ,boolean verbose){
+    if(node_label == null || node_label.isEmpty()){
+      if(verbose) System.out.println("lookup:: given node_label is null or an empty string");
+      return null;
+    }
+
+    // Try the map
+    Node node = this.node_map.get(node_label);
+    if(node != null){
+      node.put("label" ,node_label);
+      if(verbose) System.out.println("lookup:: found from map: " + node);
+      return node;
+    }
+    // At this point, node will be null
+
+    // The map lookup failed, let's try the function recognizer list
+    Node match_result = null;
+    for (RecognizerF func : this.recognizer_f_list){
+      Node match_result = func.apply(node_label); 
+      if("matched".equals(match_result.get("status"))){
+        node = match_result;
+        break;
+      }
+    }
+
+    if(verbose){
+      if(node != null) System.out.println("lookup:: found from recognizer function: " + node);
+      else System.out.println("lookup:: failed to find label: " + node_label);
+    }
+
+    return node;
+  }
+  public Node lookup(Label node_label){
+    return lookup(node_label ,true);
+  }
+
+  // Mark aware lookup function
+  public Node lookup_marked_good(Label node_label ,boolean verbose){
+    Node node = lookup(node_label ,verbose);
+    if(node != null && marked_good_q(node)) return node;
+    return null;
+  }
+  public Node lookup_marked_good(Label node_label){
+    return lookup_marked_good(node_label ,true);
+  }
+
+  /*
+    Given `root_node_label_list` of a DAG, applies `node_function` to each node in a
+    depth-first traversal order. Returns a set of error tokens encountered
+    during traversal.
+
+    `mark_form_graph` must be run on the DAG before this function is called, or
+    `lookup_marked_good` will not function correctly.
+  */
+  public TokenSet all_DAG_DF(LabelList root_node_label_list ,BiConsumer<Node ,TokenSet> node_function ,boolean verbose){
+    if(verbose) System.out.println("all_DAG_DF::");
+
+    TokenSet error_token_set = new HashSet<>();
+
+    boolean accept_arg_list = true;
+    if(node_function == null){
+      error_token_set.add("null_node_function");
+      accept_arg_list = false;
+    }
+    if(!(node_function instanceof BiFunction)){
+      error_token_set.add("node_function_not_a_function");
+      accept_arg_list = false;
+    }
+    if(root_node_label_list == null){
+      error_token_set.add("null_root_node_label_list");
+      accept_arg_list = false;
+    }
+    if(root_node_label_list.isEmpty()){
+      error_token_set.add("empty_root_node_label_list");
+      accept_arg_list = false;
+    }
+    if(!accept_arg_list) return error_token_set;
+
+    TokenSet visited = new HashSet<>();
+    List<Node> in_traversal_order = new ArrayList<>();
+
+    Stack<Label> stack = new Stack<>();
+    root_node_label_list.forEach(stack::push);
+
+    while( !stack.isEmpty() ){
+      Label node_label = stack.pop();
+
+      Node node = lookup_marked_good(node_label ,verbose);
+      if(node == null){
+        error_token_set.add("lookup_fail");
+        continue;
+      }
+
+      if(visited.contains(node.get("label"))) continue;
+      visited.add((Label)node.get("label"));
+
+      in_traversal_order.add(node);
+
+      stack.addAll( (LabelList)node.get("neighbor") );
+    }
+
+    Collections.reverse(in_traversal_order);
+    for(Node node : in_traversal_order){
+      node_function.apply(node ,error_token_set);
+    }
+
+    return error_token_set;
+  }
+  public TokenSet all_DAG_DF(LabelList root_node_label_list ,BiConsumer<Node ,TokenSet> node_function){
+    return all_DAG_DF(root_node_label_list ,node_function ,true);
+  }
+
+  /*--------------------------------------------------------------------------------
+    run the build scripts
+    depends upon is_acyclic having already marked up the graph.
+  */
+
+  // A dependency is "good" if it is marked good, and for leaf or path, if the
+  // corresponding file exists
+  public boolean good_dependency_q(LabelList node_labels){
+    return node_labels.stream().allMatch(node_label -> {
+        Node node = lookup_marked_good(node_label);
+        if( node == null ) return false;
+        if(
+          ("path".equals(node.get("type")) || "leaf".equals(node.get("type")) )
+          && !file_exists_q( (Label) node.get("label") )
+        ){
+          return false;
+        }
+        return true;
+      });
+  }
+
+  /*
+    Given a node label and a list of node labels, returns true if the file at the
+    node label in the first argument is newer than all the files at the
+    corresponding node labels in the second list.
+  */
+  public boolean newer_than_all(Label node_label ,LabelList node_label_list) throws IOException {
+    Path node_path = Paths.get(node_label);
+    if (!Files.exists(node_path)) return false;
+
+    long node_last_modified = Files.getLastModifiedTime(node_path).toMillis();
+
+    return node_label_list.stream().allMatch(label -> {
+        try {
+          Path path = Paths.get(label);
+          if (!Files.exists(path)) return false;
+          long last_modified = Files.getLastModifiedTime(path).toMillis();
+          return node_last_modified > last_modified;
+        } catch (IOException e){
+          return false;
+        }
+      });
+  }
+
+  public boolean can_be_built_q(Node node){
+    if( !marked_good_q(node) ) return false;
+    if(
+       ( "symbol".equals(node.get("type")) || "path".equals(node.get("type")) )
+        && !good_dependency_q( (LabelList)node.get("neighbor") ) 
+    ){
+      return false;
+    }
+    if( 
+       "leaf".equals( node.get("type") ) 
+       && !file_exists_q( (Label)node.get("label") )
+    ){
+      return false;
+    }
+    return true;
+  }
+
+  // `can_be_build_q` must be true for this to be meaningful:
+  public boolean should_be_built_q(Node node ,boolean verbose) throws IOException {
+    if ("leaf".equals(node.get("type"))) return false;
+    if ("symbol".equals(node.get("type"))) return true;
+    if ("path".equals(node.get("type"))) return !newer_than_all((Label) node.get("label") ,(LabelList) node.get("neighbor"));
+    
+    if (verbose){
+      System.out.println("should_be_build_q:: unrecognized node type, so assuming it should not be built.");
+    }
+    return false;
+  }
+  public boolean should_be_built_q(Node node) throws IOException {
+    return should_be_built_q(node ,true);
+  }
+
+  /*
+    Runs the build scripts, assuming the graph has been marked up already.
+  */
+  public void run_build_scripts_f(LabelList root_node_label_list ,boolean verbose) throws IOException {
+
+    if(root_node_label_list.isEmpty()) return;
+
+    TokenSet error_token_set = new HashSet<>(); // used to catch return values
+
+    System.out.println("run_build_script:: Checking if graph is well formed.");
+    error_token_set = mark_form_graph(root_node_label_list);
+    if(error_token_set != null && !error_token_set.isEmpty()){
+      System.out.println("Graph is not well-formed. Expect build problems. Errors:");
+      error_token_set.forEach(token -> System.out.println("  - " + token));
+    } else {
+      System.out.println("Graph is well-formed. Proceeding with build.");
+    }
+
+    // Define the node function
+    BiConsumer<Node ,TokenSet> node_function = (node ,error_token_set_2) -> {
+      if(!can_be_built_q(node)){
+        System.out.println("run_build_scripts_f:: Skipping build for " + node.get("label") + " due to problems with dependencies.");
+        return;
+      }
+      if(!should_be_built_q(node)){
+        if(verbose) System.out.println("run_build_scripts_f:: " + node.get("label") + " already up to date");
+        return;
+      }
+
+      // Build the target
+      System.out.println("run_build_scripts_f:: Running build script for " + node.get("label"));
+      // Assuming node.build() is a method in the Map or a related object
+      // Replace this with the actual build function for the node
+      // node.build();
+
+      // For path nodes, check if the build updated the target path
+      if("path".equals(node.get("type")) && should_be_built_q(node)){
+        System.out.println("run_build_scripts_f:: Build failed for " + node.get("label"));
+        set_mark(node ,"build_failed");
+      }
+    };
+
+    System.out.println("run_build_scripts_f:: running ...");
+    error_token_set = all_DAG_DF(root_node_label_list ,node_function ,verbose);
+    if(error_token_set != null){
+      error_token_set.forEach(error -> System.out.println("run_build_scripts_f::all_DAG_DF:: " + error));
+    }
+  }
+  public void run_build_scripts_f(LabelList root_node_label_list) throws IOException {
+    run_build_scripts_f(root_node_label_list ,true);
+  }
+
+}
diff --git a/developer/deprecated/Label.java b/developer/deprecated/Label.java
new file mode 100644 (file)
index 0000000..7802cf8
--- /dev/null
@@ -0,0 +1,35 @@
+package Ariadne;
+
+/*
+A node label.
+
+*/
+public class Label {
+    private final String value;
+
+    public Label(String value) {
+        this.value = value;
+    }
+
+    public String get() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Label label = (Label) o;
+        return value.equals(label.value);
+    }
+
+    @Override
+    public int hashCode() {
+        return value.hashCode();
+    }
+}
diff --git a/developer/deprecated/Token.java b/developer/deprecated/Token.java
new file mode 100644 (file)
index 0000000..1d1c36f
--- /dev/null
@@ -0,0 +1,35 @@
+package Ariadne;
+
+/*
+An error token.
+
+*/
+public class Token {
+    private final String value;
+
+    public Token(String value) {
+        this.value = value;
+    }
+
+    public String get() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Token token = (Token) o;
+        return value.equals(token.value);
+    }
+
+    @Override
+    public int hashCode() {
+        return value.hashCode();
+    }
+}
diff --git a/developer/javac/File.java b/developer/javac/File.java
new file mode 100644 (file)
index 0000000..d3cd0b4
--- /dev/null
@@ -0,0 +1,73 @@
+package Ariadne;
+
+import java.io.*;
+import java.nio.file.*;
+import java.util.*;
+
+public class File {
+  static boolean debug = false;
+
+  public static Map<String, String> unpack_file_path(String file_fp) {
+    if (debug) System.out.println("unpack_file_path::file_fp: " + file_fp);
+
+    // Use java.io.File explicitly to avoid conflict with the custom File class
+    java.io.File file = new java.io.File(file_fp);
+    String parent_dp = (file.getParent() != null) ? file.getParent() : "";
+
+    if (!parent_dp.isEmpty() && !parent_dp.endsWith(java.io.File.separator)) {
+      parent_dp += java.io.File.separator;
+    }
+
+    String file_fn = file.getName();
+    String file_fn_base = file_fn;
+    String file_fn_ext = "";
+
+    int last_index = file_fn.lastIndexOf('.');
+    if (last_index > 0) {
+      file_fn_base = file_fn.substring(0, last_index);
+      if (last_index + 1 < file_fn.length()) {
+        file_fn_ext = file_fn.substring(last_index + 1);
+      }
+    }
+
+    Map<String, String> ret_val = new HashMap<>();
+    ret_val.put("dp", parent_dp);
+    ret_val.put("fn", file_fn);
+    ret_val.put("fn_base", file_fn_base);
+    ret_val.put("fn_ext", file_fn_ext);
+
+    if (debug) System.out.println("unpack_file_path::ret_val: " + ret_val);
+
+    return ret_val;
+  }
+
+  public static boolean file_exists_q(String fp_string) {
+    Path fp_object = Paths.get(fp_string);
+    return Files.exists(fp_object);
+  }
+
+  /*
+    Given a target_fp and a list of list of dependency_fp.
+
+    Returns false if either the target is newer than all the dependencies, or one
+    of the specified files is missing. Otherwise returns true.
+  */
+  public static boolean newer_than_all(String target_fp_string, List<String> dependency_fp_list) throws IOException {
+    Path target_fp_object = Paths.get(target_fp_string);
+    if (!Files.exists(target_fp_object)) return false;
+
+    long target_last_modified_time = Files.getLastModifiedTime(target_fp_object).toMillis();
+
+    return dependency_fp_list.stream().allMatch(dependency_fp -> {
+      try {
+        Path dependency_fp_object = Paths.get(dependency_fp);
+        if (!Files.exists(dependency_fp_object)) return false;
+        long dependency_last_modified_time = Files.getLastModifiedTime(dependency_fp_object).toMillis();
+        return target_last_modified_time > dependency_last_modified_time;
+      } catch (IOException e) {
+        return false;
+      }
+    });
+  }
+
+}
diff --git a/developer/javac/Graph.java b/developer/javac/Graph.java
deleted file mode 100644 (file)
index 3052338..0000000
+++ /dev/null
@@ -1,685 +0,0 @@
-package Ariadne;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Stack;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-
-public class Graph {
-
-  /*--------------------------------------------------------------------------------
-   type aliases
-  */
-  public class TokenSet extends HashSet<Token>{}
-  public class LabelList extends ArrayList<Label> {}
-  public interface Node extends Map<Label ,Object>{}
-  public interface NodeList extends List<Node>{}
-  public interface RecognizerF extends Function<Label ,Node>{}
-  public class RecognizerFList extends ArrayList<RecognizerF> {}
-
-  /*--------------------------------------------------------------------------------
-   instance data 
-  */
-
-  private static boolean debug = true;
-  private Map<Label ,Node> node_map;
-  private RecognizerFList recognizer_f_list;
-
-  /*--------------------------------------------------------------------------------
-    constructors
-  */
-
-  public AriadneGraph(Map<Label ,Node> node_map ,RecognizerFList recognizer_f_list){
-    if (node_map == null && recognizer_f_list == null) {
-      System.err.println("AriadneGraph: At least one of 'node_map' (Map) or 'recognizer_f_list' (List) must be provided.");
-      System.exit(1);
-    }
-
-    // Initialize node_map and recognizer_f_list to empty collections if they are null
-    this.node_map = (node_map != null) ? node_map : new HashMap<Label ,Node>();
-
-    this.recognizer_f_list = (recognizer_f_list != null) ? recognizer_f_list : new RecognizerFList();
-  }
-
-  /*--------------------------------------------------------------------------------
-   file utilities
-  */
-
-  public static Map<String ,String> unpack_file_path(String file_fp){
-    if(debug) System.out.println("unpack_file_path::file_fp: " + file_fp);
-
-    File file = new File(file_fp);
-    String parent_dp = (file.getParent() != null) ? file.getParent() : "";
-
-    if( !parent_dp.isEmpty() && !parent_dp.endsWith(File.separator) ){
-      parent_dp += File.separator;
-    }
-
-    String file_fn = file.getName();
-    String file_fn_base = file_fn;
-    String file_fn_ext = "";
-
-    int last_index = file_fn.lastIndexOf('.');
-    if(last_index > 0){
-      file_fn_base = file_fn.substring(0 ,last_index);
-      if( last_index + 1 < file_fn.length() ){
-        file_fn_ext = file_fn.substring(last_index + 1);
-      }
-    }
-
-    Map<String ,String> ret_val = new HashMap<>();
-    ret_val.put("dp" ,parent_dp);
-    ret_val.put("fn" ,file_fn);
-    ret_val.put("fn_base" ,file_fn_base);
-    ret_val.put("fn_ext" ,file_fn_ext);
-
-    if(debug) System.out.println("unpack_file_path::ret_val: " + ret_val);
-
-    return ret_val;
-  }
-
-  public static boolean file_exists_q(Label node_label){
-    Path node_path = Paths.get(node_label);
-    return Files.exists(node_path);
-  }
-
-  /*--------------------------------------------------------------------------------
-    About nodes
-
-    A leaf type node specifies a path to a file that should not be deleted by
-    in clean operations. Typically this is the source code. We could add a
-    tool to lock permissions on these before a build, so that the build
-    scripts will also not mess with them (unless they change permissions).
-
-    If the user has multiple dependency graphs defined, a node with no
-    dependencies in one graph, might have dependencies in another.
-
-    An error type node, is one that was found to not have a type, or
-    was constructed by the tool to be a place older, perhaps for a
-    node label that was not found.
-
-  */
-  public static TokenSet all_node_type_set = new TokenSet();
-  static {
-    all_node_type_set.add(new Token("symbol"));
-    all_node_type_set.add(new Token("path"));
-    all_node_type_set.add(new Token("leaf"));
-    all_node_type_set.add(new Token("error"));
-  }
-
-  public static TokenSet persistent_node_mark_set = new TokenSet();
-  static {
-    persistent_node_mark_set.add(new Token("cycle_member"));
-    persistent_node_mark_set.add(new Token("wellformed"));
-    persistent_node_mark_set.add(new Token("build_failed"));
-    persistent_node_mark_set.add(new Token("null_node"));
-  }
-
-  public static boolean leaf_q(Node node){
-    return node != null && "leaf".equals(node.get("type"));
-  }
-
-  public static boolean has_mark(Node node){
-    return node != null && node.get("mark") != null && !( (TokenSet)node.get("mark") ).isEmpty();
-  }
-
-  public static void set_mark(Node node ,Token mark){
-    if( node.get("mark") == null ){
-      node.put("mark" ,new HashTokenSet());
-    }
-    ( (TokenSet)node.get("mark") ).add(mark);
-  }
-
-  public static void clear_mark(Node node ,Token mark){
-    if( node != null && node.get("mark") != null ){
-      ( (TokenSet) node.get("mark") ).remove(mark);
-    }
-  }
-
-  public static boolean marked_good_q(Node node){
-    return node != null && node.get("mark") != null
-      && ( (TokenSet)node.get("mark") ).contains("wellformed")
-      && !( (TokenSet)node.get("mark") ).contains("cycle_member")
-      && !( (TokenSet)node.get("mark") ).contains("build_failed");
-  }
-
-
-  /*--------------------------------------------------------------------------------
-   Well-formed Node Check
-  */
-
-  public static TokenSet form_condition_set = new TokenSet();
-  static {
-    form_condition_set.add(new Token("no_node"));
-    form_condition_set.add(new Token("node_must_have_label"));
-    form_condition_set.add(new Token("label_must_be_string"));
-    form_condition_set.add(new Token("node_must_have_type"));
-    form_condition_set.add(new Token("bad_node_type"));
-    form_condition_set.add(new Token("neighbor_value_must_be_list"));
-    form_condition_set.add(new Token("neighbor_reference_must_be_string"));
-    form_condition_set.add(new Token("neighbor_label_not_in_graph"));
-    form_condition_set.add(new Token("mark_property_value_must_be_set"));
-    form_condition_set.add(new Token("unregistered_mark"));
-    form_condition_set.add(new Token("missing_required_build_code"));
-    form_condition_set.add(new Token("leaf_given_neighbor_property"));
-    form_condition_set.add(new Token("leaf_given_build_property"));
-  }
-
-  // given a node, collects a description of its form, returns a set form condition tokens
-  public static TokenSet wellformed_node_q(Node node){
-    TokenSet form_error_set = new HashSet<>();
-
-    if(node == null){
-      form_error_set.add("null_node");
-      return form_error_set;
-    }
-
-    if( !node.containsKey("label") )
-      form_error_set.add("node_must_have_label");
-    else if( !(node.get("label") instanceof Label) )
-      form_error_set.add("label_must_be_string");
-
-    if( !node.containsKey("type") )
-      form_error_set.add("node_must_have_type");
-    else if( !(node.get("type") instanceof String) || !all_node_type_set.contains(node.get("type")) )
-      form_error_set.add("bad_node_type");
-
-    if( node.containsKey("neighbor") ){
-      if( !(node.get("neighbor") instanceof List) )
-        form_error_set.add("neighbor_value_must_be_list");
-      else if( !((List<?>) node.get("neighbor")).stream().allMatch(it -> it instanceof Label) )
-        form_error_set.add("neighbor_reference_must_be_string");
-    }
-
-    if( node.containsKey("mark") ){
-      if( !(node.get("mark") instanceof Set) )
-        form_error_set.add("mark_property_value_must_be_set");
-      else if( !((Set<?>) node.get("mark")).stream().allMatch(it -> persistent_node_mark_set.contains(it)) )
-        form_error_set.add("unregistered_mark");
-    }
-
-    if( "path".equals(node.get("type")) && (!node.containsKey("build") || !(node.get("build") instanceof Runnable)) )
-      form_error_set.add("missing_required_build_code");
-
-    if( "leaf".equals(node.get("type")) ){
-      if( node.containsKey("neighbor") ) form_error_set.add("leaf_given_neighbor_property");
-      if( node.containsKey("build") ) form_error_set.add("leaf_given_build_property");
-    }
-
-    return form_error_set;
-  }
-
-  // given a node, potentially marks it as wellformed, returns one of 'wellformed' or 'malformed'
-  public static Token wellformed_mark_node(Node node ,boolean verbose){
-    if(debug){
-      if(node != null){
-        System.out.println("wellformed_mark_node::node: " + node);
-      }else{
-        System.out.println("wellformed_mark_node given a null node");
-      }
-    }
-
-    TokenSet form_errors = wellformed_node_q(node);
-    if( form_errors.isEmpty() ){
-      set_mark( node ,"wellformed" );
-      return "wellformed";
-    }
-
-    // At this point we know that form_errors is not empty
-    if(verbose){
-      if( node != null && node.get("label") != null && ((Label)node.get("label")).length() > 0 ){
-        System.out.print( "node " + node.get("label") + " is malformed due to:" );
-      }else{
-        System.out.print("anonymous node is malformed due to:");
-      }
-      for(Token error : form_errors){
-        System.out.print(" " + error);
-      }
-      System.out.println("");
-    }
-
-    return "malformed";
-  }
-  public Token wellformed_mark_node(Node node){
-    return wellformed_mark_node(node ,true);
-  }
-
-  // given a node_label, potentially marks the corresponding node as 'wellformed', returns a token set.
-  // Tokens included "undefined_node", "malformed", and "defactor_leaf".
-  public TokenSet wellformed_mark_node_label(Label node_label ,boolean verbose){
-    TokenSet ret_value = new HashSet<>();
-    Node node = lookup(node_label);
-    if(node == null){
-      ret_value.add("undefined_node");
-      return ret_value;
-    }
-    if( "malformed".equals(wellformed_mark_node(node ,verbose)) ){
-      ret_value.add("malformed");
-    }
-    if( ((List<?>)node.get("neighbor")).isEmpty() ){
-      ret_value.add("defacto_leaf"); // might not be `type:leaf`
-    }
-    return ret_value;
-  }
-  public TokenSet wellformed_mark_node_label(Label node_label){
-    return wellformed_mark_node_label(node_label ,true);
-  }
-
-
-  /*--------------------------------------------------------------------------------
-   A well formed graph checker.  Traverses entire graph and marks nodes
-   that are not well formed or that are part of a cycle.
-
-   This must be run on the graph for `lookup_marked_good` to work.
-
-   Each node_label must be a string and not empty.
-
-   Subleties here because we have not yet determined if the nodes we are
-   wellformed (after all ,that is what we are determining here).
-
-   If we want to attempt to build 'islands' of things that might be located on
-   the far side of cycles ,then modify the cycle finder to return a list of
-   cycles (i.e. a list of lists) ,then use each of cycle definition (a list) as
-   the root nodes for further search.
-
-   `path_stack` is a stack of LabelList. The first entry is a clone of the list of
-   root nodes, referenced by label. Each subsequent list is a clone of the
-   neighbor list of the leftmost node of the prior entry.
-
-   `path` is a list of the left most nodes, referenced by label, of the entries
-   on the path stack. This is the path to our current location in the tree.
-  */
-
-
-  private boolean find_and_remove_cycle(List<LabelList> path_stack ,LabelList path ,boolean verbose){
-
-    if( path.size() <= 1 ) return false; // 0 or 1 length path can't have a cycle
-
-    // we want to know if the most recent node added to the path occurs at a point earlier
-    // in the path.
-    int rightmost_index = path.size() - 1;
-    Label recent_node_label = path.get( rightmost_index );
-    int cycle_start_index = path.indexOf(recent_node_label);
-    if( cycle_start_index == -1 ){
-      System.err.println("find_and_remove_cycle:: indexOf does not find index of known list member");
-      return false;
-    }
-    Boolean has_cycle =  cycle_start_index < rightmost_index;
-    if(!has_cycle) return false;
-
-    if(verbose) System.out.print("mark_form_graph_descend:: dependency cycle found:");
-    for( Label cycle_node_label : path.subList(cycle_start_index ,path.size()) ){
-      if(verbose) System.out.print(" " + cycle_node_label);
-      Node cycle_node = lookup(cycle_node_label);
-      if( cycle_node.get("mark") == null ){
-        cycle_node.put( "mark" ,new HashTokenSet() );
-      }
-      ( (TokenSet)cycle_node.get("mark") ).add("cycle_member");
-    }
-    if(verbose) System.out.println("");
-
-    // We cannot continue searching after the loop, so we pop back to treat
-    // the first node in the loop as though a leaf node.
-    path_stack.subList( cycle_start_index + 1 ,path_stack.size() ).clear();
-
-    return true;
-  }
-
-  private static TokenSet mark_form_graph_descend_set = new TokenSet();
-  static {
-    mark_form_graph_descend_set.add(new Token("empty_path_stack"));
-    mark_form_graph_descend_set.add(new Token("cycle_found"));
-    mark_form_graph_descend_set.add(new Token("undefined_node"));
-    mark_form_graph_descend_set.add(new Token("exists_malformed"));
-    mark_form_graph_descend_set.add(new Token("defacto_leaf"));
-  }
-
-  private TokenSet mark_form_graph_descend( List<LabelList> path_stack ,boolean verbose ){
-    TokenSet ret_value = new HashSet<>();
-    if(path_stack.isEmpty()){
-      if(verbose) System.out.println( "mark_form_graph_descend:: given empty path_stack to descend from" );
-      ret_value.add( "empty_path_stack" );
-      return ret_value;
-    }
-
-    LabelList local_path = new ArrayList<>();
-    for(LabelList path : path_stack){
-      local_path.add( path.get(0) );
-    }
-    Label local_node_label = local_path.get( local_path.size() - 1 );
-  
-    do{
-      
-      if( find_and_remove_cycle(path_stack ,local_path ,verbose) ){
-        ret_value.add("cycle_found");
-        return ret_value;
-      }
-
-      TokenSet wellformed_mark_node_label_result = wellformed_mark_node_label(local_node_label ,verbose);
-      ret_value.addAll( wellformed_mark_node_label_result );
-      if( 
-         wellformed_mark_node_label_result.contains("undefined_node") 
-         || wellformed_mark_node_label_result.contains("defacto_leaf") 
-      ){
-        return ret_value;
-      }
-
-      // Descend further into the tree.
-      path_stack.add( new ArrayList<>((LabelList) lookup(local_node_label).get("neighbor")) );
-      local_node_label = (LabelList)lookup(local_node_label).get("neighbor").get(0);
-      local_path.add(local_node_label);
-
-    }while(true);
-  }
-
-   
-  /*
-    Given root_node_label_list, marks up the graph and returns a set possibly
-    containing 'all_wellformed' and 'cycles_exist'.
-
-    Marks potentially added to each node include 'cycle_member' and 'wellformed'.
-    Note that these marks are independent.
-  */
-  public TokenSet mark_form_graph(LabelList root_node_label_list ,boolean verbose){
-    TokenSet ret_value = new HashSet<>();
-    boolean exists_malformed = false;
-    TokenSet result; // used variously
-
-    if( root_node_label_list.isEmpty() ) return ret_value;
-
-    // Initialize the DFS tree iterator.
-    List<LabelList> path_stack = new ArrayList<>();
-    path_stack.add( new ArrayList<>(root_node_label_list) );
-
-    // iterate over left side tree descent, not ideal as it starts at the
-    // root each time, but avoids complexity in the cycle detection logic.
-    do{
-      result = mark_form_graph_descend(path_stack ,verbose);
-      if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
-      if( result.contains("undefined_node") ) exists_malformed = true;
-      if( result.contains("exists_malformed") ) exists_malformed = true;
-
-      // increment the iterator to the next leftmost path
-      LabelList top_list = path_stack.get( path_stack.size() - 1 );
-      top_list.remove(0);
-      if( top_list.isEmpty() ) path_stack.remove( path_stack.size() - 1 );
-
-    }while( !path_stack.isEmpty() );
-
-    if( !exists_malformed ) ret_value.add("all_wellformed");
-
-    if( verbose ){
-      if( exists_malformed ) System.out.println("one or more malformed nodes were found");
-      boolean exists_cycle = ret_value.contains("cycle_exists");
-      if( exists_cycle ) System.out.println("one or more cyclic dependency loop found");
-      if( exists_malformed || exists_cycle ) System.out.println("will attempt to build unaffected nodes");
-    }
-
-    return ret_value;
-  }
-  public TokenSet mark_form_graph(LabelList root_node_label_list){
-    return mark_form_graph(root_node_label_list ,true);
-  }
-
-
-  /*--------------------------------------------------------------------------------
-    Graph traversal
-  */
-
-  // Given a node label, looks it up in the dependency graph, returns the node or null
-  public Node lookup(Label node_label ,boolean verbose){
-    if(node_label == null || node_label.isEmpty()){
-      if(verbose) System.out.println("lookup:: given node_label is null or an empty string");
-      return null;
-    }
-
-    // Try the map
-    Node node = this.node_map.get(node_label);
-    if(node != null){
-      node.put("label" ,node_label);
-      if(verbose) System.out.println("lookup:: found from map: " + node);
-      return node;
-    }
-    // At this point, node will be null
-
-    // The map lookup failed, let's try the function recognizer list
-    Node match_result = null;
-    for (RecognizerF func : this.recognizer_f_list){
-      Node match_result = func.apply(node_label); 
-      if("matched".equals(match_result.get("status"))){
-        node = match_result;
-        break;
-      }
-    }
-
-    if(verbose){
-      if(node != null) System.out.println("lookup:: found from recognizer function: " + node);
-      else System.out.println("lookup:: failed to find label: " + node_label);
-    }
-
-    return node;
-  }
-  public Node lookup(Label node_label){
-    return lookup(node_label ,true);
-  }
-
-  // Mark aware lookup function
-  public Node lookup_marked_good(Label node_label ,boolean verbose){
-    Node node = lookup(node_label ,verbose);
-    if(node != null && marked_good_q(node)) return node;
-    return null;
-  }
-  public Node lookup_marked_good(Label node_label){
-    return lookup_marked_good(node_label ,true);
-  }
-
-  /*
-    Given `root_node_label_list` of a DAG, applies `node_function` to each node in a
-    depth-first traversal order. Returns a set of error tokens encountered
-    during traversal.
-
-    `mark_form_graph` must be run on the DAG before this function is called, or
-    `lookup_marked_good` will not function correctly.
-  */
-  public TokenSet all_DAG_DF(LabelList root_node_label_list ,BiConsumer<Node ,TokenSet> node_function ,boolean verbose){
-    if(verbose) System.out.println("all_DAG_DF::");
-
-    TokenSet error_token_set = new HashSet<>();
-
-    boolean accept_arg_list = true;
-    if(node_function == null){
-      error_token_set.add("null_node_function");
-      accept_arg_list = false;
-    }
-    if(!(node_function instanceof BiFunction)){
-      error_token_set.add("node_function_not_a_function");
-      accept_arg_list = false;
-    }
-    if(root_node_label_list == null){
-      error_token_set.add("null_root_node_label_list");
-      accept_arg_list = false;
-    }
-    if(root_node_label_list.isEmpty()){
-      error_token_set.add("empty_root_node_label_list");
-      accept_arg_list = false;
-    }
-    if(!accept_arg_list) return error_token_set;
-
-    TokenSet visited = new HashSet<>();
-    List<Node> in_traversal_order = new ArrayList<>();
-
-    Stack<Label> stack = new Stack<>();
-    root_node_label_list.forEach(stack::push);
-
-    while( !stack.isEmpty() ){
-      Label node_label = stack.pop();
-
-      Node node = lookup_marked_good(node_label ,verbose);
-      if(node == null){
-        error_token_set.add("lookup_fail");
-        continue;
-      }
-
-      if(visited.contains(node.get("label"))) continue;
-      visited.add((Label)node.get("label"));
-
-      in_traversal_order.add(node);
-
-      stack.addAll( (LabelList)node.get("neighbor") );
-    }
-
-    Collections.reverse(in_traversal_order);
-    for(Node node : in_traversal_order){
-      node_function.apply(node ,error_token_set);
-    }
-
-    return error_token_set;
-  }
-  public TokenSet all_DAG_DF(LabelList root_node_label_list ,BiConsumer<Node ,TokenSet> node_function){
-    return all_DAG_DF(root_node_label_list ,node_function ,true);
-  }
-
-  /*--------------------------------------------------------------------------------
-    run the build scripts
-    depends upon is_acyclic having already marked up the graph.
-  */
-
-  // A dependency is "good" if it is marked good, and for leaf or path, if the
-  // corresponding file exists
-  public boolean good_dependency_q(LabelList node_labels){
-    return node_labels.stream().allMatch(node_label -> {
-        Node node = lookup_marked_good(node_label);
-        if( node == null ) return false;
-        if(
-          ("path".equals(node.get("type")) || "leaf".equals(node.get("type")) )
-          && !file_exists_q( (Label) node.get("label") )
-        ){
-          return false;
-        }
-        return true;
-      });
-  }
-
-  /*
-    Given a node label and a list of node labels, returns true if the file at the
-    node label in the first argument is newer than all the files at the
-    corresponding node labels in the second list.
-  */
-  public boolean newer_than_all(Label node_label ,LabelList node_label_list) throws IOException {
-    Path node_path = Paths.get(node_label);
-    if (!Files.exists(node_path)) return false;
-
-    long node_last_modified = Files.getLastModifiedTime(node_path).toMillis();
-
-    return node_label_list.stream().allMatch(label -> {
-        try {
-          Path path = Paths.get(label);
-          if (!Files.exists(path)) return false;
-          long last_modified = Files.getLastModifiedTime(path).toMillis();
-          return node_last_modified > last_modified;
-        } catch (IOException e){
-          return false;
-        }
-      });
-  }
-
-  public boolean can_be_built_q(Node node){
-    if( !marked_good_q(node) ) return false;
-    if(
-       ( "symbol".equals(node.get("type")) || "path".equals(node.get("type")) )
-        && !good_dependency_q( (LabelList)node.get("neighbor") ) 
-    ){
-      return false;
-    }
-    if( 
-       "leaf".equals( node.get("type") ) 
-       && !file_exists_q( (Label)node.get("label") )
-    ){
-      return false;
-    }
-    return true;
-  }
-
-  // `can_be_build_q` must be true for this to be meaningful:
-  public boolean should_be_built_q(Node node ,boolean verbose) throws IOException {
-    if ("leaf".equals(node.get("type"))) return false;
-    if ("symbol".equals(node.get("type"))) return true;
-    if ("path".equals(node.get("type"))) return !newer_than_all((Label) node.get("label") ,(LabelList) node.get("neighbor"));
-    
-    if (verbose){
-      System.out.println("should_be_build_q:: unrecognized node type, so assuming it should not be built.");
-    }
-    return false;
-  }
-  public boolean should_be_built_q(Node node) throws IOException {
-    return should_be_built_q(node ,true);
-  }
-
-  /*
-    Runs the build scripts, assuming the graph has been marked up already.
-  */
-  public void run_build_scripts_f(LabelList root_node_label_list ,boolean verbose) throws IOException {
-
-    if(root_node_label_list.isEmpty()) return;
-
-    TokenSet error_token_set = new HashSet<>(); // used to catch return values
-
-    System.out.println("run_build_script:: Checking if graph is well formed.");
-    error_token_set = mark_form_graph(root_node_label_list);
-    if(error_token_set != null && !error_token_set.isEmpty()){
-      System.out.println("Graph is not well-formed. Expect build problems. Errors:");
-      error_token_set.forEach(token -> System.out.println("  - " + token));
-    } else {
-      System.out.println("Graph is well-formed. Proceeding with build.");
-    }
-
-    // Define the node function
-    BiConsumer<Node ,TokenSet> node_function = (node ,error_token_set_2) -> {
-      if(!can_be_built_q(node)){
-        System.out.println("run_build_scripts_f:: Skipping build for " + node.get("label") + " due to problems with dependencies.");
-        return;
-      }
-      if(!should_be_built_q(node)){
-        if(verbose) System.out.println("run_build_scripts_f:: " + node.get("label") + " already up to date");
-        return;
-      }
-
-      // Build the target
-      System.out.println("run_build_scripts_f:: Running build script for " + node.get("label"));
-      // Assuming node.build() is a method in the Map or a related object
-      // Replace this with the actual build function for the node
-      // node.build();
-
-      // For path nodes, check if the build updated the target path
-      if("path".equals(node.get("type")) && should_be_built_q(node)){
-        System.out.println("run_build_scripts_f:: Build failed for " + node.get("label"));
-        set_mark(node ,"build_failed");
-      }
-    };
-
-    System.out.println("run_build_scripts_f:: running ...");
-    error_token_set = all_DAG_DF(root_node_label_list ,node_function ,verbose);
-    if(error_token_set != null){
-      error_token_set.forEach(error -> System.out.println("run_build_scripts_f::all_DAG_DF:: " + error));
-    }
-  }
-  public void run_build_scripts_f(LabelList root_node_label_list) throws IOException {
-    run_build_scripts_f(root_node_label_list ,true);
-  }
-
-}
diff --git a/developer/javac/Graph.javax b/developer/javac/Graph.javax
new file mode 100644 (file)
index 0000000..8da55d1
--- /dev/null
@@ -0,0 +1,82 @@
+package Ariadne;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Graph {
+
+  /*--------------------------------------------------------------------------------
+   type aliases
+  */
+  public class TokenSet extends HashSet<Token>{}
+  public class LabelList extends ArrayList<Label> {}
+  public interface Node extends Map<Label ,Object>{}
+  public interface NodeList extends List<Node>{}
+  public interface RecognizerF extends Function<Label ,Node>{}
+  public class RecognizerFList extends ArrayList<RecognizerF> {}
+
+  /*--------------------------------------------------------------------------------
+    constructors
+  */
+  public AriadneGraph(Map<Label ,Node> node_map ,RecognizerFList recognizer_f_list){
+    if (node_map == null && recognizer_f_list == null) {
+      System.err.println("AriadneGraph: At least one of 'node_map' (Map) or 'recognizer_f_list' (List) must be provided.");
+      System.exit(1);
+    }
+
+    // Initialize each of node_map and recognizer_f_list to empty collections if null
+    this.node_map = (node_map != null) ? node_map : new HashMap<Label ,Node>();
+    this.recognizer_f_list = (recognizer_f_list != null) ? recognizer_f_list : new RecognizerFList();
+  }
+
+
+
+  /*--------------------------------------------------------------------------------
+   instance data 
+  */
+  private static boolean debug = true;
+  private Map<Label ,Node> node_map;
+  private RecognizerFList recognizer_f_list;
+
+
+    // Internal map to store nodes, with labels as keys
+    private Map<Label, Node> node_map;
+
+    // Constructor to initialize the node map
+    public Graph(Map<Label, Node> node_map) {
+        if (node_map == null) {
+            this.node_map = new HashMap<>();
+        } else {
+            this.node_map = node_map;
+        }
+    }
+
+    // Lookup method to find a node by its label
+    public Node lookup(Label node_label, boolean verbose) {
+        if (node_label == null || node_label.isEmpty()) {
+            if (verbose) {
+                System.out.println("lookup:: given node_label is null or empty.");
+            }
+            return null;
+        }
+
+        // Try to retrieve the node from the map
+        Node node = this.node_map.get(node_label);
+
+        if (verbose) {
+            if (node != null) {
+                System.out.println("lookup:: found node: " + node);
+            } else {
+                System.out.println("lookup:: node not found for label: " + node_label);
+            }
+        }
+
+        return node;
+    }
+
+    // Overloaded lookup method with default verbosity (true)
+    public Node lookup(Label node_label) {
+        return lookup(node_label, true);
+    }
+
+}
diff --git a/developer/javac/Label.java b/developer/javac/Label.java
deleted file mode 100644 (file)
index 7802cf8..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-package Ariadne;
-
-/*
-A node label.
-
-*/
-public class Label {
-    private final String value;
-
-    public Label(String value) {
-        this.value = value;
-    }
-
-    public String get() {
-        return value;
-    }
-
-    @Override
-    public String toString() {
-        return value;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        Label label = (Label) o;
-        return value.equals(label.value);
-    }
-
-    @Override
-    public int hashCode() {
-        return value.hashCode();
-    }
-}
diff --git a/developer/javac/Token.java b/developer/javac/Token.java
deleted file mode 100644 (file)
index 1d1c36f..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-package Ariadne;
-
-/*
-An error token.
-
-*/
-public class Token {
-    private final String value;
-
-    public Token(String value) {
-        this.value = value;
-    }
-
-    public String get() {
-        return value;
-    }
-
-    @Override
-    public String toString() {
-        return value;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        Token token = (Token) o;
-        return value.equals(token.value);
-    }
-
-    @Override
-    public int hashCode() {
-        return value.hashCode();
-    }
-}
diff --git a/developer/shell/build b/developer/shell/build
deleted file mode 100755 (executable)
index 7477f8e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/env bash
-
-java BuildGraph "$@"
diff --git a/developer/tool/clean b/developer/tool/clean
deleted file mode 100755 (executable)
index f061336..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-
-# Ensure REPO_HOME and ENV_DEV_BUILD are set
-if [ -z "$REPO_HOME" ]; then
-  echo "release:: REPO_HOME is not set."
-  exit 1
-fi
-
-if [ -z "$ENV_DEV_BUILD" ]; then
-  echo "release:: ENV_DEV_BUILD is not set."
-  exit 1
-fi
-
-set -x
-cd "$REPO_HOME"/developer
-rm -rf scratch_pad/*
-set +x
-
-echo "clean complete."
diff --git a/developer/tool/clean_all b/developer/tool/clean_all
deleted file mode 100755 (executable)
index 79fbc8f..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-
-# Ensure REPO_HOME and ENV_DEV_BUILD are set
-if [ -z "$REPO_HOME" ]; then
-  echo "release:: REPO_HOME is not set."
-  exit 1
-fi
-
-if [ -z "$ENV_DEV_BUILD" ]; then
-  echo "release:: ENV_DEV_BUILD is not set."
-  exit 1
-fi
-
-wrapper=build
-
-set -x
-cd "$REPO_HOME"/developer
-rm -rf scratch_pad/*
-rm -f jvm/Ariadne.jar
-rm -f shell/{$wrapper}
-set +x
-
-echo "clean_all complete."
diff --git a/developer/tool/clean_dist b/developer/tool/clean_dist
deleted file mode 100755 (executable)
index 91c23c3..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-
-# Ensure REPO_HOME and ENV_DEV_BUILD are set
-if [ -z "$REPO_HOME" ]; then
-  echo "release:: REPO_HOME is not set."
-  exit 1
-fi
-
-if [ -z "$ENV_DEV_BUILD" ]; then
-  echo "release:: ENV_DEV_BUILD is not set."
-  exit 1
-fi
-
-# Define release directory
-release_dir="$REPO_HOME"/release_candidate
-wrapper=build
-
-set -x
-cd "$REPO_HOME"/developer
-rm -rf scratch_pad/*
-rm -f jvm/Ariadne.jar
-rm -f shell/"$wrapper"
-rm -f "$release_dir"/Ariadne.jar
-rm -f "$release_dir"/{$wrapper}
-set +x
-
-echo "clean_dist complete."
diff --git a/developer/tool/clean_javac b/developer/tool/clean_javac
new file mode 100755 (executable)
index 0000000..58109d4
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+# remove  all files created by make's call to `javac`
+
+script_name=$(basename ${BASH_SOURCE[0]})
+if [ -z "$REPO_HOME" ]; then
+  echo "$script_name:: REPO_HOME is not set."
+  exit 1
+fi
+if [ -z "$ENV_DEVELOPER_LOCAL" ]; then
+  echo "release:: ENV_DEV_BUILD is not set."
+  exit 1
+fi
+
+set -x
+cd "$REPO_HOME"/developer
+rm -rf scratch_pad/*
+set +x
+
+echo "$script_name done."
diff --git a/developer/tool/clean_make b/developer/tool/clean_make
new file mode 100755 (executable)
index 0000000..fbdec11
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/bash
+# remove all files made by `make`
+
+script_name=$(basename ${BASH_SOURCE[0]})
+error=false
+if [ -z "$REPO_HOME" ]; then
+  echo "$script_name:: REPO_HOME is not set."
+  error=true
+fi
+if [ -z "$ENV_DEVELOPER_LOCAL" ]; then
+  echo "$script_name:: ENV_DEV_BUILD is not set."
+  error=true
+fi
+if [ error ]; then
+  exit 1
+fi
+
+wrapper=build
+
+set -x
+cd "$REPO_HOME"/developer
+rm -rf scratch_pad/*
+rm -f jvm/Ariadne.jar
+rm -f shell/{$wrapper}
+set +x
+
+echo "$script_name done."
diff --git a/developer/tool/clean_release b/developer/tool/clean_release
new file mode 100755 (executable)
index 0000000..c358078
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+# remove file made by `make` and by `release`
+
+script_name=$(basename ${BASH_SOURCE[0]})
+error=false
+if [ -z "$REPO_HOME" ]; then
+  echo "$script_name:: REPO_HOME is not set."
+  error=true
+fi
+if [ -z "$ENV_DEVELOPER_LOCAL" ]; then
+  echo "$script_name:: ENV_DEV_BUILD is not set."
+  error=true
+fi
+if [ error ]; then
+  exit 1
+fi
+
+# Define release directory
+release_dir="$REPO_HOME"/release_candidate
+wrapper=build
+
+set -x
+cd "$REPO_HOME"/developer
+rm -rf scratch_pad/*
+rm -f jvm/Ariadne.jar
+rm -f shell/"$wrapper"
+rm -f "$release_dir"/Ariadne.jar
+rm -f "$release_dir"/{$wrapper}
+set +x
+
+echo "$script_name done."
index cf656f3..3b03a8f 100644 (file)
@@ -1,46 +1,49 @@
 #!/usr/bin/env bash
 
-echo "REOP_HOME/developer/tool/env"
+## input guards
 
-# The build environment. 
-#
-env_error=false
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
-  echo "env_build:: This script must be sourced, not executed."
-  env_error=true
-fi
-if [ -z "$ENV_DEVELOPER" ]; then
-  echo "env_build:: script can only be run from the developer environment"
-  env_error=true
+# instead of bailing, we will go fetch what we need
+env_should_be="tool_shared/bespoke/env"
+if [ "$ENV" != "$env_should_be" ]; then
+  source "$REPO_HOME"/"$env_should_be"
 fi
-if [ "$env_error" = true ]; then
+
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+  echo "$script_name:: This script must be sourced, not executed."
   exit 1
 fi
 
-# goovy use has been deprecated
-# export GROOVY_HOME="$REPO_HOME/tool_shared/third_party/groovy-4.0.9"
+## so we can do the build
+
+cd "$REPO_HOME"/developer/
+
+export ENV=$(script_fp)
+export PROJECT="$PROJECT"_developer
 
-# third party tools we will use
-export JAVA_HOME="$REPO_HOME/tool_shared/third_party/jdk-11"
 export PATH=\
+"$REPO_HOME"/developer/tool/\
+:"$REPO_HOME"/tool_shared/bespoke/\
 "$JAVA_HOME"/bin\
 :"$PATH"
 
-# where the tool build finds its class files:
+## so we can run locally the stuff we built,
+
 export CLASSPATH=\
 "$REPO_HOME"/developer/jvm\
 :"$REPO_HOME"/developer/jvm/Ariadne.jar\
 :"$JAVA_HOME"/lib\
 :"$CLASSPATH"
 
-# after we building the tool we might want to run it
-# the shell directory holds the bash UI wrappers
 export PATH=\
 "$REPO_HOME"/developer/shell\
 :"$PATH"
 
-export ENV_DEV_BUILD=true
-echo "${BASH_SOURCE[0]}" "complete"
+## misc
+
+# make .githolder and .gitignore visible
+alias ls="ls -a"
+
+echo "$ENV complete."
 
 
 
index 4040acb..a345dd9 100755 (executable)
@@ -1,7 +1,8 @@
 #!/bin/env bash
 
-if [ -z "$ENV_DEVELOPER" ]; then
-  echo "make_PaintIt:: script can only be run from the developer environment"
+script_name=$(basename ${BASH_SOURCE[0]})
+if [ -z "$ENV_DEVELOPER_LOCAL" ]; then
+  echo "$script_name:: script can only be run from the developer environment"
   return 1
 fi
 
@@ -34,12 +35,14 @@ mkdir -p shell
 
 wrapper=build
 for file in $wrapper;do
-  cat > shell/$file << EOL
-  #!/bin/bash
-  java com/ReasoningTechnology/Ariadne/$file
-  EOL
-  chmod +x shell/$file
+
+cat > shell/$file << EOL
+#!/bin/bash
+java com/ReasoningTechnology/Ariadne/$file
+EOL
+chmod +x shell/$file
+
 done
 
-echo "make done"
+echo "$script_name done."
 
index 1edfafd..2675ba9 100755 (executable)
@@ -1,20 +1,23 @@
 #!/bin/bash
 
-# Ensure REPO_HOME and ENV_DEV_BUILD are set
+script_name=$(basename ${BASH_SOURCE[0]})
 if [ -z "$REPO_HOME" ]; then
-  echo "release:: REPO_HOME is not set."
+  echo "$script_name:: REPO_HOME is not set."
   exit 1
 fi
 
-if [ -z "$ENV_DEV_BUILD" ]; then
-  echo "release:: ENV_DEV_BUILD is not set."
+if [ -z "$ENV_LOCAL_DEVELOPER" ]; then
+  echo "$script_name:: ENV_DEV_BUILD is not set."
   exit 1
 fi
 
-# Define release directory
-release_dir="$REPO_HOME/release_candidate"
+release_dir="$REPO_HOME/release"
+shell_dir="$REPO_HOME/developer/shell"
+Ariadne_jar_fp="$REPO_HOME/developer/jvm/Ariadne.jar"
+wrapper=build
+
+
 
-# Create release directory if it doesn't exist
 if [ ! -d "$release_dir" ]; then
   mkdir -p "$release_dir"
 fi
@@ -42,16 +45,12 @@ install_file() {
  
 echo "Starting release process..."
 
-# Paths to shell wrappers and JAR file
-shell_dir="$REPO_HOME/developer/shell"
-Ariadne_jar_fp="$REPO_HOME/developer/jvm/Ariadne.jar"
-
 # Install the JAR file
 install_file "$Ariadne_jar_fp" "$release_dir" "ug+r"
 
 # Install shell wrappers
-for wrapper in black blue green; do
+for wrapper in $wrapper; do
   install_file "$shell_dir/$wrapper" "$release_dir" "ug+r+x"
 done
 
-echo "Release process completed successfully."
+echo "$script_name done."
index 105bbc3..92e1703 100644 (file)
@@ -3,52 +3,78 @@
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
+
   <title>Directory Structure Description</title>
-  <style>
-    body {
-      background-color: hsl(0, 0%, 0%);
-      color: hsl(42, 100%, 80%);
-      font-family: monospace;
-      padding: 20px;
-      margin: 0;
-    }
-    .page {
-      padding: 20px;
-      margin: 20px auto;
-      max-width: 800px;
-      background-color: hsl(0, 0%, 10%);
-      box-shadow: 0 0 10px hsl(42, 100%, 50%);
-    }
-    ul, li {
-      list-style-type: none;
-    }
-    li::before {
-      content: "📁 ";
-      margin-right: 5px;
-    }
-    li {
-      margin-bottom: 5px;
-    }
-    .description {
-      margin-left: 10px;
-      color: hsl(42, 100%, 60%);
-    }
-    h1 {
-      text-align: center;
-      color: hsl(42, 100%, 80%);
-      text-transform: uppercase;
-      margin-bottom: 20px;
-    }
-    h2 {
-      color: hsl(42, 100%, 80%);
-      text-transform: uppercase;
-      margin-top: 40px;
-    }
-    p {
-      color: hsl(42, 100%, 70%);
-      margin-bottom: 20px;
-    }
-  </style>
+<style>
+  html {
+    font-size: 16px; /* This will be the base for rem units */
+  }
+
+  body {
+    font-family: 'Noto Sans JP', Arial, sans-serif;
+    background-color: hsl(0, 0%, 10%);
+    color: hsl(42, 100%, 80%);
+    padding: 2rem;
+    margin: 0;
+  }
+
+  .page {
+    padding: 1.25rem; /* 20px */
+    margin: 1.25rem auto; /* 20px */
+    max-width: 46.875rem; /* 750px */
+    background-color: hsl(0, 0%, 0%);
+    box-shadow: 0 0 0.625rem hsl(42, 100%, 50%); /* 10px */
+  }
+
+  ul, li {
+    font-size: 1rem; /* Keeping default font size */
+    list-style-type: none;
+  }
+
+  li::before {
+    content: "📁 ";
+    margin-right: 0.3125rem; /* 5px */
+  }
+
+  li {
+    margin-bottom: 0.3125rem; /* 5px */
+  }
+
+  .description {
+    margin-left: 0.625rem; /* 10px */
+    color: hsl(42, 100%, 75%);
+  }
+
+  code {
+    font-family: 'Courier New', Courier, monospace;
+    background-color: hsl(0, 0%, 25%);
+    color: hsl(42, 100%, 90%);
+    padding: 0.125rem 0.25rem; /* 2px 4px */
+    border-radius: 0.1875rem; /* 3px */
+    font-size: 90%;
+  }
+
+  h1 {
+    text-align: center;
+    color: hsl(42, 100%, 84%);
+    text-transform: uppercase;
+    margin-bottom: 1.25rem; /* 20px */
+  }
+
+  h2 {
+    color: hsl(42, 100%, 84%);
+    text-transform: uppercase;
+    margin-top: 2.5rem; /* 40px */
+  }
+
+  p {
+    color: hsl(42, 100%, 90%);
+    margin-bottom: 1.25rem; /* 20px */
+    text-align: justify;
+  }
+</style>
+
 </head>
 <body>
 
     <h2>Reference</h2>
 
     <ul>
-      <li>/var/user_data/Thomas-developer/Ariadne</li>
+      <li>Ariadne/<span class="description">aka REPO_HOME, top level owned by the project administrator.</span></li>
       <ul>
-        <li>developer/ <span class="description">The workspace for developers to organize source code, build scripts, and development-specific tools.</span></li>
+        <li>developer/ <span class="description">Workspace for the developer. Has the source code, build scripts, and development-specific tools.</span></li>
         <ul>
-          <li>deprecated/ <span class="description">Archived files and older versions no longer in active use.</span></li>
-          <li>document/ <span class="description">Developer-level documentation for building and understanding the project.</span></li>
-          <li>javac/ <span class="description">Java source files prepared for compilation.</span></li>
-          <li>jvm/ <span class="description">Compiled Java bytecode files for the project.</span></li>
-          <li>scratch_pad/ <span class="description">Temporary storage for intermediate files during development.</span></li>
-          <li>shell/ <span class="description">Shell scripts intended to be part of the project release or build automation.</span></li>
-          <li>tool/ <span class="description">Developer-specific tools used for building or managing development tasks.</span></li>
+          <li>deprecated/ <span class="description">Files and older versions being viewed, perhaps part of a refactoring effort.</span></li>
+          <li>document/ <span class="description">Documentation on developing and building the project.</span></li>
+          <li>javac/ <span class="description">Java source files for compilation.</span></li>
+          <li>jvm/ <span class="description">Compiled Java bytecode files for the project, typically a jar for a Java project.</span></li>
+          <li>scratch_pad/ <span class="description">Temporary storage typically for intermediate files created during build.</span></li>
+          <li>shell/ <span class="description">Shell scripts intended to be part of the project release. (These are not tools.)</span></li>
+          <li>tool/ <span class="description">Tools created by the developer, used for development tasks.</span></li>
         </ul>
-        <li>document/ <span class="description">Top-level project documentation for project managers or contributors.</span></li>
-        <li>release_candidate/ <span class="description">Builds and packages ready for testing and release preparation.</span></li>
-        <li>scratch_pad/ <span class="description">Temporary storage for project management tasks.</span></li>
-        <li>tester/ <span class="description">Files and tools for managing and running test environments.</span></li>
+        <li>document/ <span class="description">General documentation about the project.</span></li>
+        <li>release/ <span class="description">Release candidate for testing. Becomes the release on the release branch.</span></li>
+        <li>scratch_pad/ <span class="description">Temporary storage for project administration tasks.</span></li>
+        <li>tester/ <span class="description">Workspace for the tester. Has the test bench, tests, and test scripts.</span></li>
         <ul>
-          <li>document/ <span class="description">Test-specific documentation for procedures and setups.</span></li>
+          <li>document/ <span class="description">Test-specific documentation.</span></li>
           <li>test0/ <span class="description">Test case 0 environment and associated scripts.</span></li>
           <li>test1/ <span class="description">Test case 1 environment and associated scripts.</span></li>
           <li>test2/ <span class="description">Test case 2 environment and associated scripts.</span></li>
           <li>tool/ <span class="description">Tools needed for testing and managing the test environment.</span></li>
         </ul>
-        <li>tool/ <span class="description">Project management/administration specific tools.</span></li>
+        <li>tool/ <span class="description">Project administration specific tools.</span></li>
         <li>tool_shared/ <span class="description">Tools shared across project roles.</span></li>
         <ul>
-          <li>bespoke/ <span class="description">Tools developed specifically for this project.</span></li>
+          <li>bespoke/ <span class="description">Shared tools developed within this project.</span></li>
           <li>customized/ <span class="description">Modified versions of third-party tools adapted for the project.</span></li>
           <li>document/ <span class="description">Documentation related to shared tools and setup.</span></li>
-          <li>third_party/ <span class="description">Tools sourced from third-party vendors or open-source projects.</span></li>
+          <li>third_party/ <span class="description">Shared tools sourced from third-party vendors or open-source projects. These have their own independent licenses,</span></li>
         </ul>
         <li>LICENSE.txt <span class="description">The project license detailing usage and distribution terms.</span></li>
         <li>README.md <span class="description">A general overview and introduction to the project.</span></li>
       </ul>
     </ul>
 
-
     <h2>Name origin and rationale</h2>
 
-    <p>Typically, developers and project administrators do not employ a semantic
-      system for naming directories, but more commonly use conventional
-      placeholder names. The intended purpose of files in a directory with a
-      placeholder name then must be inferred from experience or inspection of
-      the files, or learned from other people or from documents.</p>
+    <p>Developers and project administrators typically do not employ a semantic system for
+      naming directories, but more commonly use conventional placeholder
+      names. The intended purpose of files in a directory with a placeholder
+      name then must be inferred from experience or inspection of the files, or
+      learned from documents or other people.</p>
 
-    <p>For example, a directory named 'exe/' probably derives its name from the
+    <p>For example, a directory named <code>exe/</code> probably derives its name from the
       fact that the contained files have their executable permission bit set;
       however, such a directory will not contain all such files.  There might
-      even be some files in an `exe/` directory that do not have their
-      executable permission bit set. The two concepts being an `exe/` file
-      (i.e. being a file in an `exe/` directory) and being an executable file
-      are not identical. The actual intended meaning of being an `exe/` file
+      even be some files in an <code>exe/</code> directory that do not have their
+      executable permission bit set. The two concepts being an <code>exe/</code> file
+      (i.e. being a file in an <code>exe/</code> directory) and being an executable file
+      are not identical. The actual intended meaning of being an <code>exe/</code> file
       will sometimes be that the contained files are applications available to a
       user, or that they are tools available for use in a project.
     </p>
       property-based file system. In such a system a number of files and
       agents are defined. Then we can ask questions about their relationships.
       Files with a relationship to the developer are collected, and this
-      becomes the `developer/` directory. In a similar manner we get the
-      directories, `tester/', and `javac/`.  In this latter case the
+      becomes the <code>developer/</code> directory. In a similar manner we get the
+      directories, <code>tester/</code>, and <code>javac/</code>.  In this latter case the
       agent is a compiler rather than a role.
       </p>
       
-    <p>However, when attempting applying this method in practice it became
-      apparent that the recognition of relationships was insufficient.  Consider
-      the directories `deprecated` and `scratch_pad`. There is no
-      'Mr. Deprecated' or 'Mr. Scratch_Pad' who the contained files are
-      for. (And this conclusion is not for the lack of trying. Even mythological
-      beings did not suffice as agents.) Rather than saying a file has a
-      relationship with an agent, these properties (directory names) are states
-      imposed by decree by an agent on a file.  Perhaps the developer, has
+    <p>When attempting to apply the <code>is-for</code> property in practice it
+      became apparent that using this sole property was insufficient.  Consider
+      the directories <code>deprecated/</code> and <code>scratch_pad/</code>. There is no
+      <em>Mr. Deprecated</em> or <em>Mr. Scratch_Pad</em> who the contained
+      files are for. (And this conclusion is not for the lack of trying. Even
+      mythological beings did not suffice as agents.) Rather than being for an
+      agent, the files collected in such a directory have in common a state of
+      being that was imposed upon them by decree.  Perhaps the developer, has
       decreed that a file is now deprecated, or a build script has decreed that
       it is a scratch_pad file. Such decrees are typically more dynamic than the
       relationship properties. Also, these properties are disconnected from the
-      contents of the file.  When, for example, we say a file is for the java
-      compiler we can surmise something about its contents. However when we say
-      a file is `deprecated` we can surmise nothing about its contents.
+      contents of the file.  When, for example, a file has the property of being
+      for the java compiler, we gain some information about its contents. In the
+      universe of possible messages sent through a file, such a file will
+      contain text that is proposed to be java syntax conforming. In contrast,
+      when we learn that a file is <code>deprecated/</code> we gain no
+      information about the contents of the file, because any file can
+      be <code>deprecated</code>, independent of its contents.
       </p>
 
     <p>To understand a directory name within this system, one can imagine
       reading said name as part of a sentence that integrates the
-      property. Consider two property names: 'is-a' and 'is-for'. For example,
-      "Each file in the <code>document/</code> directory is a document," or
-      "Each file in the <code>developer/</code> directory is for the developer."
-      Although the property name is not carried over to the conventional file
-      system, we can typically infer what it must be. It is beyond the scope of
-      discussion here, but in actuality, our collections are defined by
-      predicates that are given a file's properties and relationships as
-      arguments, where the predicate resolves to true if and only if the file
-      belongs to the collection. Now wouldn't that be interesting if we
-      instead derived a probability?
+      property. Consider two property names: <code>is-a</code>
+      and <code>is-for</code>. For example, "Each file in
+      the <code>document/</code> directory <code>is-a</code> document," or "Each
+      file in the <code>developer/</code> directory <code>is-for</code> the
+      developer."  Although the property name is not carried over from the
+      property based file system to the conventional file system, we can
+      typically infer what it must have been. (It is beyond the scope of
+      discussion here, but in actuality, property based file system collections
+      are defined by predicates. Each predicate is given a file's properties and
+      relationships as arguments, then resolves to true if and only if the file
+      belongs to the collection. Now wouldn't that be interesting if we instead
+      derived a probability?)
     </p>
 
     <p>It is uncommon for a property value to be plural. While it is not
       property. Hence when we make a file collection based on a shared property,
       then carry that over as a directory name in a conventional file system,
       the resulting directory name will often be singular.  This pattern can be
-      observed in the case of the `document/` directory, as shown in the prior
-      paragraph.
+      observed in the case of the <code>document/</code> directory, as shown in
+      the prior paragraph.
     </p>
 
   </div>
diff --git a/release/.githolder b/release/.githolder
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/release/black b/release/black
new file mode 100755 (executable)
index 0000000..d94d9cb
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/bash
+java com/ReasoningTechnology/PaintIt/Black
diff --git a/release/blue b/release/blue
new file mode 100755 (executable)
index 0000000..387f82a
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/bash
+java com/ReasoningTechnology/PaintIt/Blue
diff --git a/release/green b/release/green
new file mode 100755 (executable)
index 0000000..9611a51
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/bash
+java com/ReasoningTechnology/PaintIt/Green
diff --git a/release_candidate/.githolder b/release_candidate/.githolder
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/release_candidate/black b/release_candidate/black
deleted file mode 100755 (executable)
index d94d9cb..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-java com/ReasoningTechnology/PaintIt/Black
diff --git a/release_candidate/blue b/release_candidate/blue
deleted file mode 100755 (executable)
index 387f82a..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-java com/ReasoningTechnology/PaintIt/Blue
diff --git a/release_candidate/green b/release_candidate/green
deleted file mode 100755 (executable)
index 9611a51..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-java com/ReasoningTechnology/PaintIt/Green
diff --git a/tester/javac/TestBench.java b/tester/javac/TestBench.java
new file mode 100644 (file)
index 0000000..0a9de3d
--- /dev/null
@@ -0,0 +1,29 @@
+import java.util.Map;
+
+public class TestBench {
+
+  public static void runTest_Map(Map<String, Boolean> test_map){
+    int totalTest_Map = test_map.size();
+    int passedTest_Map = 0;
+    int failedTest_Map = 0;
+
+    for( Map.Entry<String, Boolean> test : test_map.entrySet() ){
+      try{
+        if( test.getValue() ){
+          passedTest_Map++;
+        } else{
+          System.out.println( "failed: " + test.getKey() );
+          failedTest_Map++;
+        }
+      } catch(Exception e){
+        System.out.println( "failed: " + test.getKey() );
+        failedTest_Map++;
+      }
+    }
+
+    System.out.println("Total test_map run: " + totalTest_Map);
+    System.out.println("Total test_map passed: " + passedTest_Map);
+    System.out.println("Total test_map failed: " + failedTest_Map);
+  }
+
+}
diff --git a/tester/tool/env b/tester/tool/env
new file mode 100644 (file)
index 0000000..a630a47
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+## input guards
+
+# instead of bailing, we will go fetch what we need
+env_should_be="tool_shared/bespoke/env"
+if [ "$ENV" != "$env_should_be" ]; then
+  source "$REPO_HOME"/"$env_should_be"
+fi
+
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+  echo "$script_name:: This script must be sourced, not executed."
+  exit 1
+fi
+
+cd "$REPO_HOME"/tester/
+
+export ENV=$(script_fp)
+export PROJECT="$PROJECT"_tester
+
+export PATH=\
+"$REPO_HOME"/tester/tool/\
+:"$REPO_HOME"/tool_shared/bespoke/\
+"$JAVA_HOME"/bin\
+:"$PATH"
+
+export CLASSPATH=\
+:"$REPO_HOME"/developer/release/Ariadne.jar\
+:"$JAVA_HOME"/lib\
+:"$CLASSPATH"
+
+echo "$ENV complete."
diff --git a/tester/tool/env_tester b/tester/tool/env_tester
deleted file mode 100644 (file)
index eb6b6c7..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-# environment common to all tests
-# each test directory also has an environment
-
-export JAVA_HOME="$REPO_HOME/tool/jdk-11"
-export GROOVY_HOME="$REPO_HOME/tool/groovy-4.0.9"
-
-export PATH=\
-"$REPO_HOME"/release_candidate\
-:"$JAVA_HOME"/bin\
-:"$GROOVY_HOME"/bin\
-:"$PATH"
diff --git a/tester/tool/make b/tester/tool/make
new file mode 100755 (executable)
index 0000000..c9aca35
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/env bash
+
+script_name=$(basename ${BASH_SOURCE[0]})
+if [ -z "$ENV_TESTER_LOCAL" ]; then
+  echo "make.sh:: script can only be run in the tester  environment"
+  env_error=true
+fi
+
+
+
+
+echo "$script_name done."
diff --git a/tester/tool/make.sh b/tester/tool/make.sh
deleted file mode 100755 (executable)
index 4ecb729..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/env bash
-
-if [ -z "$ENV_TESTER" ]; then
-  echo "make.sh:: script can only be run in the tester  environment"
-  env_error=true
-fi
-
-set -x
-
-groovyc TestGraph.groovy
diff --git a/tool/env b/tool/env
new file mode 100644 (file)
index 0000000..830913d
--- /dev/null
+++ b/tool/env
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+script_name=$(basename ${BASH_SOURCE[0]})
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+  echo "$script_name must be sourced, not executed. Exiting."
+  exit 1
+fi
+
+if [ -z "$ENV_BASE" ]; then
+  script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
+  source "${script_path}/env_base"
+fi
+
+ENV_PM=true
+
+PROJECT="$PROJECT"_administrator
+
+export PATH=\
+"$REPO_HOME"/tool\
+:"$PATH"
+
+# no sneaky hidden files
+alias ls="ls -a"
+
+export ENV_PM=true
+echo "$script_name done."
+
diff --git a/tool/env_administrator b/tool/env_administrator
deleted file mode 100644 (file)
index b85bd37..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
-  echo "This script must be sourced, not executed. Exiting."
-  return 1
-fi
-
-if [ -z "$ENV_BASE" ]; then
-  script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
-  source "${script_path}/env_base"
-fi
-
-ENV_PM=true
-
-PROJECT="$PROJECT"_administrator
-
-export PATH=\
-"$REPO_HOME"/tool\
-:"$PATH"
-
-# no sneaky hidden files
-alias ls="ls -a"
-
-
-export ENV_PM=true
-echo "${BASH_SOURCE[0]}" "complete"
diff --git a/tool/env_base b/tool/env_base
deleted file mode 100644 (file)
index ff49183..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env bash
-
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
-  echo "This script must be sourced, not executed. Exiting."
-  return 1
-fi
-
-# These are things set by the `repo` command found in the `resource` project,
-# but if you don't have that, then source this into the environment.
-
-script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
-export REPO_HOME="${script_path%/*}/"
-export PROJECT=$(basename "$REPO_HOME")
-
-PPS1="\n[$PROJECT]\n\u@\h§$(pwd)§\n> "
-PPS2=">> "
-
-echo REPO_HOME "$REPO_HOME"
-echo PROJECT "$PROJECT"
-echo "${BASH_SOURCE[0]}" "complete"
-
-export ENV_BASE=true
diff --git a/tool/env_developer b/tool/env_developer
deleted file mode 100644 (file)
index b1c5ffa..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
-  echo "This script must be sourced, not executed. Exiting."
-  return 1
-fi
-
-if [ -z "$ENV_BASE" ]; then
-  script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
-  source "${script_path}/env_base"
-fi
-
-PROJECT="$PROJECT"_developer
-
-export PATH=\
-"$REPO_HOME"/developer/tool/\
-:"$REPO_HOME"/tool_shared/bespoke/\
-:"$PATH"
-
-# so the .gitignore files can be seen:
-alias ls="ls -a"
-
-cd "$REPO_HOME"/developer/
-export ENV_DEVELOPER=true
-source tool/env
-
-echo "${BASH_SOURCE[0]}" "complete"
-
-
-
diff --git a/tool/env_tester b/tool/env_tester
deleted file mode 100644 (file)
index 7a52fe9..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
-  echo "This script must be sourced, not executed. Exiting."
-  return 1
-fi
-
-# Check if REPO_HOME is set, if not source env_base
-if [ -z "$ENV_BASE" ]; then
-  script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
-  source "${script_path}/env_base"
-fi
-
-PROJECT="$PROJECT"_TESTER
-
-export PATH=\
-"$REPO_HOME"/tester/executor/\
-:"$REPO_HOME"/tool_shared/bespoke/\
-:"$PATH"
-
-cd "$REPO_HOME"/tester/
-export ENV_TESTER=true
-source tool/env
-
-echo "${BASH_SOURCE[0]}" "complete"
diff --git a/tool_shared/bespoke/deprecate b/tool_shared/bespoke/deprecate
new file mode 100755 (executable)
index 0000000..821e7ab
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# cp subtree at <source> under file path <destination>, and make all the copied
+# files read-only. The intended use case is for moving files to a `deprecated`
+# directory. This helps prevent subsequent accidental editing.
+
+SCRIPT_NAME=$(basename "$0")
+if [ "$#" -lt 2 ]; then
+  echo "Usage: $SCRIPT_NAME <source> <destination>"
+  exit 1
+fi
+SRC="$1"
+DEST="$2"
+
+mkdir -p "$DEST"
+mv "$SRC" "$DEST"
+
+# make stuff readonly
+cd "$DEST" || exit
+chmod -R u-w,go-rwx "$DEST"
diff --git a/tool_shared/bespoke/env b/tool_shared/bespoke/env
new file mode 100644 (file)
index 0000000..1939b29
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+# environment shared by entire project
+
+# Path to this script
+script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
+
+
+
+# Path to the repo is two directories up
+export REPO_HOME="${script_path%/*/*}/"
+
+# script name and path relative to the top of the project
+script_fn(){
+  echo "$(basename ${BASH_SOURCE[0]})"
+}
+script_fp(){
+  echo "$(realpath --relative-to="$REPO_HOME" "$script_path")/$(script_fn)"
+}
+export -f script_fn script_fp
+
+# REPO_HOME relative file path to this script
+# Calling the function correctly without braces
+export ENV=$(script_fp)
+
+# this script must be sourced, sort of like Python activate
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+  echo "$ENV must be sourced, not executed. Exiting."
+  exit 1
+fi
+
+export PROJECT=$(basename "$REPO_HOME")
+
+# Third party tool(s) that the project admin kindly installed.
+# The developer and tester should use these links to find them.
+export JAVA_HOME="$REPO_HOME/tool_shared/third_party/jdk-11"
+
+# set the shell prompt to show the project
+PPS1="\n[$PROJECT]\n\u@\h§$(pwd)§\n> "
+PPS2=">> "
+
+echo ENV "$ENV"
+echo REPO_HOME "$REPO_HOME"
+echo PROJECT "$PROJECT"
+echo "$ENV completed."
+
+
diff --git a/tool_shared/bespoke/temp b/tool_shared/bespoke/temp
new file mode 100644 (file)
index 0000000..0a80f78
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+# Path to this script
+script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
+
+# Path to the repo is two directories up
+export REPO_HOME="${script_path%/*/*}/"
+
+# script name and path relative to the top of the project
+script_fn(){
+  echo "$(basename ${BASH_SOURCE[0]})"
+}
+script_fp(){
+  echo "$(realpath --relative-to="$REPO_HOME" "$script_path")/$(script_fn)"
+}
+
+# REPO_HOME relative file path to this script
+# Calling the function correctly without braces
+export ENV=$(script_fp)
+
+# Output the values
+echo "Script path: $script_path"
+echo "Repo home: $REPO_HOME"
+echo "ENV: $ENV"
index 6e696ef..a2fede5 100755 (executable)
@@ -1,4 +1,8 @@
 #!/bin/env bash
 
-echo 0.1
+# 0.1 pulled groovy files from GQL_to_Cypher build.gradle
+# 0.2 conversion to Java
+# 0.3 refactored, split into smaller pieces, made into package
+
+echo 0.3
 
diff --git a/tool_shared/bespoke/wipe_replease b/tool_shared/bespoke/wipe_replease
new file mode 100755 (executable)
index 0000000..4b92b4b
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+# remove all files in the release directory
+set -e
+
+script_name=$(basename ${BASH_SOURCE[0]})
+if [ -z "$REPO_HOME" ]; then
+  echo "$script_name:: REPO_HOME is not set."
+  exit 1
+fi
+
+set -x
+cd "$REPO_HOME"
+rm -rf release/*
+set +x
+
+echo "$script_name done."
+