breaking Graph into component layers
authorThomas Walker Lynch <xtujpz@reasoningtechnology.com>
Mon, 21 Oct 2024 17:46:28 +0000 (17:46 +0000)
committerThomas Walker Lynch <xtujpz@reasoningtechnology.com>
Mon, 21 Oct 2024 17:46:28 +0000 (17:46 +0000)
27 files changed:
developer/deprecated/Label.java [deleted file]
developer/deprecated/Token.java [deleted file]
developer/deprecated/cycle_case.java [new file with mode: 0644]
developer/document/#question_jan.txt# [new file with mode: 0644]
developer/document/#variable_suffix_conventions.txt# [new file with mode: 0644]
developer/document/RT_code_format.txt [new file with mode: 0644]
developer/document/variable_suffix_conventions.txt [new file with mode: 0644]
developer/javac/Graph.java [new file with mode: 0644]
developer/javac/Graph.javax [deleted file]
developer/javac/GraphAriadne.javax [new file with mode: 0644]
developer/javac/GraphDirectedAcyclic.java [new file with mode: 0644]
developer/javac/Label.java [new file with mode: 0644]
developer/javac/LabelList.java [new file with mode: 0644]
developer/javac/Node.java [new file with mode: 0644]
developer/javac/NodeList.java [new file with mode: 0644]
developer/javac/Production.java [new file with mode: 0644]
developer/javac/ProductionList.java [new file with mode: 0644]
developer/javac/Token.java [new file with mode: 0644]
developer/javac/TokenSet.java [new file with mode: 0644]
developer/javac/Util.java [new file with mode: 0644]
developer/temp [new file with mode: 0644]
document/readme.txt
document/todo.txt
release/Ariadne.jar
tester/javac/TestBenchAriadne.java
tester/jvm/TestBenchAriadne.jar
tool_shared/bespoke/cat_w_fn [new file with mode: 0755]

diff --git a/developer/deprecated/Label.java b/developer/deprecated/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/deprecated/Token.java b/developer/deprecated/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/deprecated/cycle_case.java b/developer/deprecated/cycle_case.java
new file mode 100644 (file)
index 0000000..040654b
--- /dev/null
@@ -0,0 +1,48 @@
+      //----------------------------------------
+      // cycle case
+      //
+      
+      List<Integer>  cycle_index_interval;
+      if( cycle_index_interval=path_find_cycle(left_path ,verbose) ){
+        ret_value.add( "cycle_found" );
+
+        int cycle_i0 = cycle_index_interval.get(0); // cycle leftmost, inclusive
+        int cycle_n = cycle_index_interval.get(1);  // cycle rightmost, inclusive
+
+        if(verbose) Util.print_list(
+          "Found cycle:" 
+          ,left_path.subList( cycle_i0 ,n +1)
+        );
+
+        // mark cycle
+        LabelList undefined_node_list;
+        int i = cycle_i0;
+        do{
+          node_label = left_path.get(i);
+          node = super.lookup(node_label);
+          if(node){
+            if( node.get("mark") == null ){
+              node.put( "mark" ,new HashTokenSet() );
+            }
+            ( (TokenSet)node.get("mark") ).add("cycle_member");
+          }else{
+            undefined_node_list.add(node_label);
+            ret_value.add( "undefined_node" );
+          }
+          i++;
+          if( i > cycle_n ) break;
+        }while(true);
+
+        if(verbose) Util.print_list(
+          "Each undefined node could not be marked as a cycle member:" 
+          ,undefined_node_list
+        );
+
+        // Reset the graph iterator to immediately top of the cycle, then return
+        // as though we had hit a leaf node. (Upon return the the i0 node will
+        // be dropped as part of incrementing the iterator.)
+        path_stack.subList(cycle_i0 + 1 ,cycle_n + 1).clear();
+
+        return ret_value;
+      } 
+
diff --git a/developer/document/#question_jan.txt# b/developer/document/#question_jan.txt#
new file mode 100644 (file)
index 0000000..9b0715c
--- /dev/null
@@ -0,0 +1,2 @@
+
+On a directed acyclic graph, is it 
\ No newline at end of file
diff --git a/developer/document/#variable_suffix_conventions.txt# b/developer/document/#variable_suffix_conventions.txt#
new file mode 100644 (file)
index 0000000..8aca090
--- /dev/null
@@ -0,0 +1,27 @@
+# Suffix Conventions
+
+## Specify interface used with variable when clarification is useful:
+
+- `_set`: Indicates that the variable holds a set of items.
+
+- `_list`: Used for variables that represent a list of items.
+
+- `_f`: Refers to a function.
+
+Instead of making a variable name plural, add the interface qualifier:
+
+   e.g. names ->  name_set  or name_list
+
+## Always a good idea to use these when working with files:
+
+- `_fp`: Refers to a file path. The part after the last slash is a file name.
+  
+- `_dp`: Refers to a directory path. By convention, the value ends in a slash.
+
+- `_fn`: Refers to a file name. Value has no slashes.
+
+- `_dn`: Refers to a directory name. Value has no slashes.
+  
+- `_fn_base`: The file name without the last dot and subsequent characters.
+  
+- `_fn_ext`: The subsequent characters after the last dot in a file name.
diff --git a/developer/document/RT_code_format.txt b/developer/document/RT_code_format.txt
new file mode 100644 (file)
index 0000000..7f85e16
--- /dev/null
@@ -0,0 +1,127 @@
+RT code formatting:
+
+This has evolved into place after decades of programming in Unix culture projects
+in multiple languages.
+
+AI tools we have used are OK with the commas, but have had difficulties
+with the padding rules for enclosures.
+
+1. Two space indentation.
+
+2. Variable Naming:
+
+   - Use **PascalCase** for namespaces and types.
+
+   - Use **snake_case** for function and variable names. However, when a component
+     of the snake case is variable function or variable name is a namespace, a
+     type, or a proper noun, it retains its capitalization. e.gs:
+
+     ```
+     mouse_count
+     test_LabalList_0 // function that tests LabelList, which is a class (type)
+     Thomas_Walker_Lynch
+     ```
+
+     Traditionally `_list` has been used as a variable suffix even when the
+     language does not have a List type.  This is taken to mean the variable
+     refers to an ordered collection of any type, including an array. It is
+     abstraction of type, analogous to the `mouse_count` example above.
+
+
+3. Binary Operators:
+
+   - One space around **binary operators** (e.g., `a + b`).
+
+   - One space around **assignment** `=` (e.g., `a = b`).
+
+   - **No space** around **sampling** assignment `=` (typically seen in `if`, `while`, etc.):
+
+     **Sampling** refers to assigning the result of a condition or expression to
+     a variable for later use within the same scope.
+
+     Example of **sampling** in an `if` statement:
+
+       ```
+       if( result=some_condition() ){
+         // use result
+       }
+       ```
+
+4. Enclosures `(...)`, `{...}`, `[...]`, '<...>':
+
+   - No space between the enclosure and the preceding identifier (e.g., `function(arg)`).
+
+   - No space after the enclosure when followed by another enclosure (e.g., `map[key]()`).
+
+     Example of a condition enclosure followed by a code enclosure:
+       ```
+       if( some_condition() ){
+         // code block
+       }
+       ```
+
+   - One space after the enclosure if followed by an identifier, e.g.,
+     `function() somethingElse`.
+
+   - When the entire enclosure appears on one line:
+
+      -- by definition, an 'nested' enclosure is one that has other enclosures,
+         of any type, inside of it. This is true independent of whatever else
+         is inside the enclosure.  These are examples of nested enclosures:
+
+         ```
+         ( o == null || getClass() != o.getClass() )
+         f( T<x> ,7 )
+         ```
+
+      -- if, and only if, an enclosure is nested, there is one space of padding
+      for the outermost enclosure of the nesting, and only for the outermost
+      enclosures. e.g.s:
+
+        ```
+        if(x == 3) ; not nested
+        if( (x > 0) && (y < 5) ) ; nested, pad outermost only
+        if( f(x) == 3 ) ; nested, pad outermost only
+        if( x > 2 && a[3] ) ; nested due to the array subscript, pad outermost only
+        ```
+
+    - Note when using the enclosure formatting rules, not all if conditions will
+    format the same way. Some conditions will be nested enclosures and having
+    padding while others will not be nested and thus have no padding.  The must
+    be formatted individually.  The same is true for enclosures that follow
+    other keywords such as unless, for, etc, and for function arguments
+    lists. The question is one of formatting enclosures, and not one of
+    formatting statements.
+
+      ```
+      f(x)
+      f( x[0] )
+      ```
+
+
+5. Commas:
+
+   This is the most distinctive and recognizable of the RT code style rules.
+
+   - One space **before** the comma (e.g., `a ,b`).
+
+   - No space **after** the comma (e.g., `a ,b`).
+
+   - **Line break before** the comma when breaking lines, but no line break after, as examples:
+
+     ```
+     a
+     ,b
+     ```
+
+     and, when a function call gets too long, perhaps due to long argument 
+     names it will look like this:
+
+     ```
+     result = some_function(
+       arg1
+      ,arg2_has_a_very_long_name_causing_the_call_to_not_fit_on_a_single_line
+      ,arg3_has_a_long_name_also_but_not_as_long_as_for_arg2
+      );
+     ```
+
diff --git a/developer/document/variable_suffix_conventions.txt b/developer/document/variable_suffix_conventions.txt
new file mode 100644 (file)
index 0000000..e5ef76e
--- /dev/null
@@ -0,0 +1,27 @@
+# Suffix Conventions
+
+## Specify interface used with variable when clarification is useful
+
+- `_set`: Indicates that the variable holds a set of items.
+
+- `_list`: Used for variables that represent a list of items.
+
+- `_f`: Refers to a function.
+
+Instead of making a variable name plural, add the interface qualifier.
+
+   e.g. names ->  name_set  or name_lisst
+
+## Always a good idea to use these when working with files
+
+- `_fp`: Refers to a file path. The part after the last slash is a file name.
+  
+- `_dp`: Refers to a directory path. By convention, the value ends in a slash.
+
+- `_fn`: Refers to a file name. Value has no slashes.
+
+- `_dn`: Refers to a directory name. Value has no slashes.
+  
+- `_fn_base`: The file name without the last dot and subsequent characters.
+  
+- `_fn_ext`: The subsequent characters after the last dot in a file name.
diff --git a/developer/javac/Graph.java b/developer/javac/Graph.java
new file mode 100644 (file)
index 0000000..3b7f970
--- /dev/null
@@ -0,0 +1,66 @@
+package com.ReasoningTechnology.Ariadne;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Graph{
+
+  /*--------------------------------------------------------------------------------
+    constructors
+  */
+
+  public Graph(Map<Label ,Node> node_map ,ProductionList 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 ProductionList();
+  }
+
+  /*--------------------------------------------------------------------------------
+   instance data 
+  */
+
+  private static boolean debug = true;
+  private Map<Label ,Node> node_map;
+  private ProductionList recognizer_f_list;
+
+  /*--------------------------------------------------------------------------------
+   interface
+  */
+
+  // 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/Graph.javax b/developer/javac/Graph.javax
deleted file mode 100644 (file)
index 8da55d1..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-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/GraphAriadne.javax b/developer/javac/GraphAriadne.javax
new file mode 100644 (file)
index 0000000..a5949fe
--- /dev/null
@@ -0,0 +1,376 @@
+package com.ReasoningTechnology.Ariadne;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AriadnaGraph extends Graph{
+
+  /*--------------------------------------------------------------------------------
+    constructors
+  */
+
+  public Graph(Map<Label ,Node> node_map ,ProductionList recognizer_f_list){
+
+  }
+
+  /*--------------------------------------------------------------------------------
+   instance data 
+  */
+
+  private static boolean debug = true;
+
+  /*--------------------------------------------------------------------------------
+    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 = super.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 = super.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) super.lookup(local_node_label).get("neighbor")) );
+      local_node_label = (LabelList)super.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
+  */
+
+  // Lookup method to find a node by its label
+  public Node lookup(Label node_label ,boolean verbose){
+    Node node = super.lookup(node_label ,verbose);
+    if(node != null && marked_good_q(node)) return node;
+    return null;
+  }
+
+  // Overloaded lookup method with default verbosity (true)
+  public Node lookup(Label node_label){
+    return lookup(node_label ,true);
+  }
+
+
+
+}
diff --git a/developer/javac/GraphDirectedAcyclic.java b/developer/javac/GraphDirectedAcyclic.java
new file mode 100644 (file)
index 0000000..3528bd8
--- /dev/null
@@ -0,0 +1,464 @@
+package com.ReasoningTechnology.Ariadne;
+
+/*
+Our build tool graph is directed from targets to dependencies.
+
+It must be cycle free. This class extends `Graph` by marking
+cycles that are found when descending from root nodes. Then
+`lookup` will not return these nodes.
+
+If:
+
+ 1. The production functions produce the same output given the
+ same input. (Which is a given for the map definition portion of
+ the graph).
+
+ 2. There are a finite number of states.
+
+ 3. Any input values that are used in the definition of the graph are not
+ changed after some point in time when the graph is said to have been 'defined'.
+
+ 4. No computed values are used for changing the graph definition.
+
+ 5. No computed values direct graph traversal.
+
+Then:
+
+  We can write an algorithm that in turn starts at each node
+  in the graph and searches for cycles, and will find all
+  cycles.
+
+  Our GraphDirectedAcyclic constructor would then not need
+  to know the root nodes of the traversal.
+
+However:
+
+  Had the graph definition been limited to the map object, and there is no
+  interaction with the build functions, we would meet the criteria.
+
+  However, our bestowed upon the user the ability to define 'production'
+  functions, which are used to build the graph dynamically at run time.
+
+  With such production functions is possible that a a graph definition would
+  emerge after a finite number of calls to the build function, and thus we still
+  meet the above criteria, and do not need to give a root node list to the
+  GraphDirectedAcyclic.
+
+  When a graph is to be used with the build tool, it is required that the graph
+  meet the criteria above ** when starting from the root nodes **.  However, it
+  is not generally required. Hence, we provide the root nodes to this function.
+
+  It is possible that the user defines one or more production functions,
+  intentionally nor not, that results in building an unbounded graph definition.
+  Further it is possible for a user to define a production that has an unbounded
+  nature that results in the cycle marking method of this class from never
+  finishing.
+
+  As examples, suppose that the a production follows the digits of pi, giving
+  a different node definition each time it is fed a label to recognize. Or
+  suppose that a production returns an ever longer node label each time it
+  is called. Such a production would cause the graph definition to be open,
+  and potentially for this cycle detection algorithm to never complete.
+
+  A production for a conventional build environment will not do these things,
+  However, a buggy production for build environment, or an unconventional
+  build environment not imagined at this time, could.
+
+  A breadth first search combined programmed as a generator would be able
+  to handle such cases without hanging. Where as this algorithm plunges
+  down depth first, and returns when it has the answer.
+
+*/
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+
+public class GraphDirectedAcyclic extends Graph{
+
+  /*--------------------------------------------------------------------------------
+    constructors
+  */
+
+  public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,List<Label> root_node_list ,boolean verbose){
+    super( node_map ,recognizer_f_list );
+
+    // Step 2: Run the cycle detection logic and mark up the graph.
+    TokenSet cycle_detection_result = mark_cycle_graph( root_node_list ,verbose );
+
+    // You can further process cycle detection results here if needed
+
+  }
+
+
+  public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,List<Label> root_node_list){
+    super( node_map ,recognizer_f_list );
+
+    // Step 2: Run the cycle detection logic and mark up the graph.
+    TokenSet cycle_detection_result = mark_cycle_graph( root_node_list ,this.debug );
+
+    // You can further process cycle detection results here if needed
+  }
+
+
+  /*--------------------------------------------------------------------------------
+   instance data extension
+  */
+
+  private static boolean debug = true;
+
+
+  /*--------------------------------------------------------------------------------
+    Interface
+
+    1. nodes are referenced by label.
+
+    2. A list is a kind of sequence. It consists of a leftmost item, subsequent
+    items, and a rightmost item.
+
+    3. A node list consists of a leftmost node, subsequent nodes, and a rightmost node.
+
+    4. `path_stack`
+        
+        The `path_stack` is a list. Each item in the stack is a node list.
+
+        The rightmost items is the top of the stack.
+
+        The leftmost item is a list of root nodes, where traversal of the graph
+        starts.
+
+        Given two adjacent items on the path stack, say e0 and e1:
+
+            Say k0 is the leftmost node on the node list e0.
+            
+            The e1 will be the neighbor (child) list from node k0.
+
+            Hence, the path stack consists of the child lists of nodes along
+            a traversal. We chose a leftmost traversal.
+
+                e0:   k_0       k_1    ... k_2  
+
+                e1:   k_0_0    k_0_1   ... k_0_2   ; children of k_0
+
+                e2:   K_0_0_0  k_0_0_1 ... k_0_0_2 ; children of k_0_0
+                
+
+    5. `path`
+
+        A list of the leftmost nodes from a path stack.  
+
+        Given that e0 is a root node, `path` will consist of 
+
+            k_0, k_0_0, k_0_0_0 ... k_0_0..._0
+
+        Within the context of a path, k_0 is the leftmost item, and k_n is the
+        rightmost item.
+
+
+    6. removing a cycle
+
+       This is a build tool. Each node corresponds to a build objective, and the
+       neighbor nodes are dependencies. A neighbor node is a child node in our
+       tree descent, and is a dependency to our build tool.
+
+       This routine is called as part of the analysis phase. Nothing is
+       built here, rather we are merely marking dependency cycles that we
+       find when descending from the root nodes.
+
+       When we find a cycle, we remove those nodes from the current traversal
+       path, because we do not went the analysis to do in circles.  It is
+       possible that there are spurs that emanate from the cycle, and following
+       these might lead to finding more cycles.
+
+       Our build tool (which is not in this file) will stop descending through
+       the graph upon finding a cycle, and the effects of this will cause
+       upstream nodes to also not be built. Hence, the cycles hidden behind
+       other cycles are irrelevant.  
+
+       However, if we want to make routine of more general use, then the
+       discovered cycles should be pushed on to a cycle stack, and then each
+       item on the cycle stack would be used as root nodes for a new cycle
+       search. Note the leftmost cycle on each recursive search on the leftmost
+       node, will be the original cycle.
+
+
+  */
+
+  /*
+    Cycle detection is based on label compares. Lookup is done to mark the node.
+
+    1. Given a path.
+
+    2. Checks if the rightmost item, k_n, in the path occurs earlier in the path, say at k_i.
+       If so, marks the nodes involved in the cycle.
+
+    3. if there is no cycle, returns null, otherwise returns the Interval [i ,n]
+
+  */
+  private List<Integer>  path_find_cycle(List<LabelList> path_stack ,LabelList path ){
+    if( path.size() <= 1 ) return null; 
+
+    int rightmost_index = path.size() - 1;
+    Label rightmost_node_label = path.get( rightmost_index );
+
+    // if there is no cycle, searching for rightmost_node_label will find itself:
+    int cycle_leftmost_index = path.indexOf(rightmost_node_label);
+    Boolean has_cycle = cycle_leftmost_index < rightmost_index;
+    if( ! has_cycle ) return null;
+
+    List<Integer> result = new ArrayList<>();
+    result.add(cycle_leftmost_index);
+    result.add(rightmost_index);
+    return result;
+  }
+
+  /*
+    When descending down the tree, if we find a cycle, we reset the iterator
+    to the top of the cycle. The caller will then return to its caller as though
+    having found a leaf node.
+  */
+  private boolean graph_descend_cycle_case( List<Label> left_path ,List<LabelList> path_stack ,boolean verbose ){
+
+    List<Integer> cycle_index_interval = path_find_cycle( left_path ,verbose );
+    if( cycle_index_interval == null ){
+      return false; // No cycle found
+    }
+
+    int cycle_i0 = cycle_index_interval.get(0); // Cycle leftmost, inclusive
+    int cycle_n = cycle_index_interval.get(1);  // Cycle rightmost, inclusive
+
+    if(verbose) Util.print_list(
+      "Found cycle:" 
+      ,left_path.subList( cycle_i0 ,n +1)
+    );
+
+    // Mark cycle members
+    LabelList undefined_node_list = new LabelList();
+    for( int i = cycle_i0; i <= cycle_n; i++ ){
+      Label node_label = left_path.get(i);
+      Node node = super.lookup( node_label );
+      if( node != null ){
+        if( node.get("mark") == null ){
+          node.put( "mark" ,new HashTokenSet() );
+        }
+        ( (TokenSet)node.get("mark") ).add( "cycle_member" );
+      }else{
+        undefined_node_list.add( node_label );
+      }
+    }
+
+    if(verbose) Util.print_list(
+      "Each undefined node could not be marked as a cycle member:" 
+      ,undefined_node_list
+    );
+
+    // Reset the graph iterator to the top of the cycle
+    path_stack.subList( cycle_i0 + 1 ,cycle_n + 1 ).clear();
+
+    return true; // cycle found
+  }
+  
+  /*
+    Given a path_stack initialized to the root nodes for the graph traversal.
+
+    Perform depth first descent from each node in succession. Algorithm
+    works from left to right in the child lists.
+
+    Searches for cycles in the descent paths.
+
+  */
+  private static TokenSet graph_descend_set = new TokenSet();
+  static {
+    graph_descend_set.add( new Token("empty_path_stack") );
+    graph_descend_set.add( new Token("cycle_found") );
+    graph_descend_set.add( new Token("undefined_node") );
+    graph_descend_set.add( new Token("leaf") );
+    graph_descend_set.add( new Token("max_depth_exceeded") );
+  }
+  private TokenSet graph_descend( List<LabelList> path_stack ,int max_depth ,boolean verbose ){
+    TokenSet ret_value = new HashTokenSet();
+
+    if( path_stack.isEmpty() ){
+      ret_value.add( "empty_path_stack" );
+      return ret_value;
+    }
+
+    // a leftmost path through the neighbor lists of the leftmost nodes
+    LabelList left_path = new ArrayList<>();
+    for( LabelList neighbor_list : path_stack ){
+      left_path.add( neighbor_list.get(0) );
+    }
+
+    // do descend
+    do{
+
+      // cycle case
+      if( graph_descend_cycle_case( left_path ,path_stack ,verbose ) ){
+        ret_value.add( "cycle_found" );
+        return ret_value;
+      }
+
+      // Non-cycle case, descend further into the tree.
+      // Increment the graph iterator (path_stack) to go down a level.
+      Label it_pt_label = path_stack.get( path_stack.size() - 1 ).get(0);
+      Node it_pt_node = super.lookup( it_pt_label );
+      if( it_pt_node == null ){
+        ret_value.add( "undefined_node" );
+        return ret_value;
+      }
+        
+      NodeList neighbor_list = it_pt_node.get("neighbor");
+      if( neighbor_list == null || neighbor_list.isEmpty() ){
+        ret_value.add( "leaf" );
+        return ret_value;
+      }
+
+      path_stack.add( new ArrayList<>((LabelList) neighbor_list) );
+      Label it_next_label = neighbor_list.get(0);
+      left_path.add( it_next_label );  // Properly add the label to the left_path
+
+      // bound the size of problem we are willing to work on
+      // set max_depth <= 0 to have this test ignored
+      if( max_depth > 0 ){
+        max_depth--;  // Typo fixed
+        if( max_depth == 0 ){
+          ret_value.add( "max_depth_exceeded" );
+          return ret_value;
+        }
+      }
+
+    }while( true ); // while descend
+  }
+
+   
+  /*
+    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.
+  */
+  // Saigens updates:
+  /*
+  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_cycle_graph( LabelList root_node_label_list ,boolean verbose ){
+    TokenSet ret_value = new HashTokenSet();
+    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, avoids complexity in cycle detection logic.
+    do{
+        result = graph_descend( path_stack ,verbose );
+        if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
+        if( result.contains("undefined_node") ) exists_malformed = true;  // Corrected flag
+
+        // 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 loops found.");
+        if( exists_malformed || exists_cycle ) System.out.println("Will attempt to build unaffected nodes.");
+    }
+
+    return ret_value;
+}
+
+public TokenSet mark_cycle_graph( LabelList root_node_label_list ){
+    return mark_cycle_graph( root_node_label_list ,true );
+}
+//------------------
+
+  public TokenSet mark_cycle_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 = graph_descend(path_stack ,verbose);
+      if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
+      if( result.contains("undefined_node") ) exists_undefined = 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_cycle_graph(LabelList root_node_label_list){
+    return mark_cycle_graph(root_node_label_list ,true);
+  }
+  */
+  /*--------------------------------------------------------------------------------
+    Graph traversal
+  */
+
+  @Override
+  public Node lookup(Label node_label, boolean verbose) {
+    Node node = super.lookup( node_label ,verbose );
+
+    // Ensure the node exists and the mark property exists
+    if( node != null ){
+      TokenSet mark = (TokenSet)node.get("mark");
+
+      // Check if the mark property exists and contains "cycle_member"
+      if( mark != null && mark.contains("cycle_member") ){
+        if( verbose ){
+          System.out.println("Node is part of a cycle and will not be returned: " + node_label);
+        }
+        return null;  // Exclude nodes in cycles
+      }
+    }
+
+    return node;
+  }
+
+  // Overloaded lookup method with default verbosity (true)
+  public Node lookup(Label node_label){
+    return lookup(node_label ,this.debug);
+  }
+
+
+
+}
diff --git a/developer/javac/Label.java b/developer/javac/Label.java
new file mode 100644 (file)
index 0000000..f65012c
--- /dev/null
@@ -0,0 +1,42 @@
+package com.ReasoningTechnology.Ariadne;
+
+/*
+  A node label.
+
+  This is a wrapper for a String. We can't instead use an alias by extending
+  String, because String is a JavaScript 'final' type.
+
+*/
+public class Label {
+  private final String value;
+
+  public Label(String value){
+    this.value = value;
+  }
+
+  public boolean isEmpty() {
+    return value.isEmpty();
+  }
+
+  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/LabelList.java b/developer/javac/LabelList.java
new file mode 100644 (file)
index 0000000..9b048d7
--- /dev/null
@@ -0,0 +1,10 @@
+// LabelList.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList;
+
+public class LabelList extends ArrayList<Label> {
+  // Constructor
+  public LabelList(){
+    super();
+  }
+}
diff --git a/developer/javac/Node.java b/developer/javac/Node.java
new file mode 100644 (file)
index 0000000..1a0cd85
--- /dev/null
@@ -0,0 +1,11 @@
+// Node.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Node extends HashMap<Label, Object> {
+  // Constructor
+  public Node(){
+    super();
+  }
+}
diff --git a/developer/javac/NodeList.java b/developer/javac/NodeList.java
new file mode 100644 (file)
index 0000000..189cf83
--- /dev/null
@@ -0,0 +1,10 @@
+// NodeList.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList;
+
+public class NodeList extends ArrayList<Node> {
+  // Constructor
+  public NodeList(){
+    super();
+  }
+}
diff --git a/developer/javac/Production.java b/developer/javac/Production.java
new file mode 100644 (file)
index 0000000..1639c56
--- /dev/null
@@ -0,0 +1,5 @@
+// Production.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.function.Function;
+
+public interface Production extends Function<Label, Node> {}
diff --git a/developer/javac/ProductionList.java b/developer/javac/ProductionList.java
new file mode 100644 (file)
index 0000000..7973193
--- /dev/null
@@ -0,0 +1,10 @@
+// ProductionList.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList;
+
+public class ProductionList extends ArrayList<Production> {
+  // Constructor
+  public ProductionList(){
+    super();
+  }
+}
diff --git a/developer/javac/Token.java b/developer/javac/Token.java
new file mode 100644 (file)
index 0000000..37d7c2b
--- /dev/null
@@ -0,0 +1,35 @@
+package com.ReasoningTechnology.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;  // No padding, not nested
+    if( o == null || getClass() != o.getClass() ) return false;  // Padded, because it's nested
+    Token token = (Token)o;
+    return value.equals( token.value );
+  }
+
+  @Override
+  public int hashCode(){
+    return value.hashCode();
+  }
+}
diff --git a/developer/javac/TokenSet.java b/developer/javac/TokenSet.java
new file mode 100644 (file)
index 0000000..a05443f
--- /dev/null
@@ -0,0 +1,10 @@
+// TokenSet.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.HashSet;
+
+public class TokenSet extends HashSet<Token> {
+  // Constructor
+  public TokenSet(){
+    super();
+  }
+}
diff --git a/developer/javac/Util.java b/developer/javac/Util.java
new file mode 100644 (file)
index 0000000..571e7e3
--- /dev/null
@@ -0,0 +1,25 @@
+package com.ReasoningTechnology.Ariadne;
+import java.util.List; 
+
+public class Util{
+  static boolean debug = false;
+
+  public static void print_list(String prefix ,List<?> item_list){
+    if( item_list == null || item_list.isEmpty() ){
+      return;
+    }
+    if( prefix != null && !prefix.isEmpty() ){
+      System.out.print(prefix);
+    }
+    int i = 0;
+    int n = item_list.size() - 1;
+    System.out.print( " '" + item_list.get(i) + "'" );
+    do{
+      i++;
+      if( i > n ) break;
+      System.out.print(", " +  "'" + item_list.get(i) + "'");
+    }while( true );
+    System.out.println(".");
+  }
+
+}
diff --git a/developer/temp b/developer/temp
new file mode 100644 (file)
index 0000000..b626df8
--- /dev/null
@@ -0,0 +1,42 @@
+--------------------------------------------------------------------------------
+javac/LabelList.java:
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList;
+
+  public class LabelList extends ArrayList<Label> {}
+
+--------------------------------------------------------------------------------
+javac/Node.java:
+package com.ReasoningTechnology.Ariadne;
+import java.util.Map;
+
+  public interface Node extends Map<Label ,Object>{}
+
+--------------------------------------------------------------------------------
+javac/NodeList.java:
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList;
+
+  public interface NodeList extends ArrayList<Node> {}
+
+--------------------------------------------------------------------------------
+javac/Production.java:
+package com.ReasoningTechnology.Ariadne;
+import java.util.function.Function;
+
+  public interface Production extends Function<Label ,Node>{}
+
+--------------------------------------------------------------------------------
+javac/ProductionList.java:
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList; 
+
+public class ProductionList extends ArrayList<Production> {}
+
+--------------------------------------------------------------------------------
+javac/TokenSet.java:
+package com.ReasoningTechnology.Ariadne;
+import java.util.HashSet;  
+
+public class TokenSet extends HashSet<Token>{}
+
index 9a7500e..32728bc 100644 (file)
@@ -1,19 +1,80 @@
 
-Building a project can be like finding your way out of Daedalus's impossible to
-solve maze. Ariadne hands you a ball of string.
+--------------------------------------------------------------------------------
+Ariadne
 
 Ariadne is a graph based build tool.
 
-Each graph node represents either a build target or symbolic objective.
+Tying to build a project is a lot like trying to find your way out of
+Daedalus's impossible to solve maze. Ariadne hands you a ball of string.
 
-Nodes have properties, and among these properties is a block of code that
-presumably either builds the target or accomplishes the symbolic objective.
+--------------------------------------------------------------------------------
+Documents
 
-Edges that link the nodes represent dependencies.
+Note the directories:  Ariadne/document, Ariadne/developer/document, 
+and Ariadne/tester/document.
 
-The build tool is given a graph. It traverses down to leaf dependencies then
-follows the string it laid out during the traversal, and works its way back up
-to achieve the root objectives.
 
-The build tool recognizes and skips over cycles and build failures.
+--------------------------------------------------------------------------------
+How It Works
+
+Everything is coded in Java, including the developers description of the
+dependency graph.
+
+A graph is made of nodes and edges.
+
+A node is dictionary. Each node has a 'label', a 'build' function, and a
+'neighbor' list.  The neighbor list holds the edges.
+
+Using Java, the developer puts the nodes in a map, keyed on the node label, or
+writes functions that when given a label, return either a a node or null.
+
+A node map looks a lot like classic make file.  Each node label is a target file
+path, the neighbor list can be listed next, and then we have the build code.
+
+Function defined nodes are very flexible. They are more powerful, and more
+flexible than make's pattern rules.  If a developer wants a function to act like
+a pattern rule, the developer can use a regular expression match on the given
+label. However, unlike make, pattern rules defined in this manner, will
+backwards chain potentially through other pattern rules.
+
+The Build function is one of the graph traversal programs. It is given a graph
+and a list of top level market targets. It traverses the graph to find leaf
+while unwinding its proverbial ball of string. Upon finding leaf nodes, it then
+follows the string back until building the targets.
+
+It is possible to define other graph traversal functions.  In fact, Ariadne can
+be used to do anything where a directed graph of functions is useful.
+
+
+--------------------------------------------------------------------------------
+Entering the Project
+
+The project has three entry points, one for each project role: developer,
+tester, and administrator. To enter the project, source the environment
+appropriate for the role, either `env_developer`, `env_tester`, or
+`env_administrator`.
+
+For example, to work on the code as a developer, start a fresh shell and:
+
+> cd Ariadne
+> . env_developer
+>  <run your IDE, I run emacs>
+
+[do work]
+
+> make
+> release
+
+[change to tester role]
+
+The `make` command you see above is a bash script. Version 1.0 of Ariadne uses a
+direct 'build it all every time' approach. Perhaps in version 2.0 or so, we will
+use a prior version of Ariadne for the build environment.
+
+
+Using Ariadne
+-------------
+
+After it is built and released the tool will appear in the Ariadne/release
+directory where it can be tested or imported into another project.
 
index 6f73ce9..c8a6c76 100644 (file)
@@ -1,10 +1,18 @@
 
-1. extract TestBench as its own project. Perhaps it can replace paintit as the
-project skeleton.
-
-2. Make a project 'skeleton' for starting other projects out of this one. `PaintIt` was a
-first draft at this, but made too early. Because this is java project I let
-`release` and `jvm` go to the repo.  Executables in other projects will be platform
-dependent, so if the directories are to be checked in, for example, `release` should
-be moved to `release_<X>` and a new empty release directory should be made.  Here
-<X> is the platform the executable can run on.
+2024-10-16T07:12:41Z[Ariadne_tester]
+
+1. TestBench should be extracted as its own project. It holds the project independent
+part of the TestBench.
+
+2. Project skeleton.
+
+  PaintIt is the current project skeleton, it needs to be updated to match the
+  structure of this project.
+
+3. Because this is a Java Project
+
+  'executables' have a standard form (the JVM) so I allowed the 'executable'
+  directories to be checked into the repo.
+
+  In the future, if we want to allow binary release, there will be multiple
+  release directories with suffixes for the target platform. 
index f2eb7a1..ad86277 100644 (file)
Binary files a/release/Ariadne.jar and b/release/Ariadne.jar differ
index 051d089..5898096 100644 (file)
@@ -2,6 +2,7 @@ package com.ReasoningTechnology.Ariadne.TestBench;
 import  com.ReasoningTechnology.Ariadne.*;
 import  com.ReasoningTechnology.TestBench.*;
 import java.util.Map;
+import java.util.ArrayList;
 import java.util.HashMap;
 
 public class TestBenchAriadne extends TestBench{
@@ -32,12 +33,137 @@ public class TestBenchAriadne extends TestBench{
     return all( conditions );
   }
 
+  public static boolean test_Label_0(){
+    boolean[] conditions = new boolean[2];
+    int i = 0;
+
+    // Test input
+    Label test_label = new Label("test");
+
+    // Expected output
+    String expected_value = "test";
+
+    // Actual output
+    conditions[i++] = test_label.get().equals(expected_value);
+    conditions[i++] = test_label.toString().equals(expected_value);
+
+    return all(conditions);
+  }
+
+  public static boolean test_Token_0(){
+    boolean[] conditions = new boolean[4];
+    int i = 0;
+
+    // Test input
+    Token token = new Token("test_value");
+
+    // Check if the value is correctly stored and retrieved
+    conditions[i++] = token.get().equals("test_value");
+
+    // Check if the string representation is correct
+    conditions[i++] = token.toString().equals("test_value");
+
+    // Check equality with another Token object with the same value
+    Token another_token = new Token("test_value");
+    conditions[i++] = token.equals( another_token );
+
+    // Check the hashCode consistency
+    conditions[i++] = token.hashCode() == another_token.hashCode();
+
+    return all(conditions);
+  }
+
+  public static boolean test_LabelList_0(){
+    LabelList label_list = new LabelList();  // Use the constructor
+
+    // Add a label and check the size
+    label_list.add(new Label("test"));
+    return label_list.size() == 1;
+  }
+
+  public static boolean test_Node_0(){
+    Node node = new Node();  // Use the constructor
+
+    // Add a key-value pair and check the map
+    node.put(new Label("key"), new Object());
+    return node.containsKey(new Label("key"));
+  }
+
+  public static boolean test_NodeList_0(){
+    NodeList node_list = new NodeList();  // Use the constructor
+
+    // Add a node and check the size
+    node_list.add(new Node());  // Use Node constructor
+    return node_list.size() == 1;
+  }
+
+  public static boolean test_Production_0(){
+    Production production = label -> new Node();  // Use the Node constructor
+
+    // Apply the production function
+    Node node = production.apply(new Label("test"));
+    return node != null;
+  }
+
+  public static boolean test_ProductionList_0(){
+    ProductionList production_list = new ProductionList();  // Use the constructor
+
+    // Add a production and check the size
+    production_list.add(label -> new Node());  // Use the Node constructor
+    return production_list.size() == 1;
+  }
+
+  public static boolean test_TokenSet_0(){
+    TokenSet token_set = new TokenSet();  // Use the constructor
+
+    // Add a token and check if it's contained in the set
+    token_set.add(new Token("test"));
+    return token_set.contains(new Token("test"));
+  }
+
+  public static boolean test_Graph_0() {
+    boolean[] conditions = new boolean[3];
+    int i = 0;
+
+    // Create an empty node map and a production list
+    Map<Label, Node> node_map = new HashMap<>();
+    ProductionList production_list = new ProductionList();
+
+    // Initialize the Graph
+    Graph graph = new Graph(node_map, production_list);
+
+    // Test that lookup returns null for a non-existent node
+    Label non_existent_label = new Label("non_existent");
+    conditions[i++] = graph.lookup(non_existent_label, false) == null;
+
+    // Add a node to the map and test lookup
+    Node test_node = new Node();
+    Label test_label = new Label("test");
+    node_map.put(test_label, test_node);
+    conditions[i++] = graph.lookup(test_label, false) == test_node;
+
+    // Test lookup with verbosity
+    conditions[i++] = graph.lookup(test_label).equals(test_node);
+
+    // Return true if all conditions are met
+    return all(conditions);
+  }
   // Method to run all tests
   public static void test_Ariadne(){
     Map<String, Boolean> test_map = new HashMap<>();
 
     // Adding tests to the map
-    test_map.put( "File_unpack_file_path_0", test_File_unpack_file_path_0() );
+    test_map.put( "test_File_unpack_file_path_0", test_File_unpack_file_path_0() );
+    test_map.put( "test_Label_0", test_Label_0() );
+    test_map.put( "test_Token_0", test_Label_0() );
+    test_map.put( "test_LabelList_0", test_LabelList_0() );
+    test_map.put( "test_Node_0", test_Node_0() );
+    test_map.put( "test_NodeList_0", test_NodeList_0() );
+    test_map.put( "test_Production_0", test_Production_0() );
+    test_map.put( "test_ProductionList_0", test_ProductionList_0() );
+    test_map.put( "test_TokenSet_0", test_TokenSet_0() );
+    test_map.put("test_Graph_0", test_Graph_0());
 
     // Run the tests using TestBench
     TestBench.run( test_map );
index 2f572ac..898301f 100644 (file)
Binary files a/tester/jvm/TestBenchAriadne.jar and b/tester/jvm/TestBenchAriadne.jar differ
diff --git a/tool_shared/bespoke/cat_w_fn b/tool_shared/bespoke/cat_w_fn
new file mode 100755 (executable)
index 0000000..09931d1
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Check if at least one file is provided
+if [ $# -eq 0 ]; then
+  echo "Usage: $0 <filename1> [filename2] ..."
+  exit 1
+fi
+
+# Loop through all the provided files
+for file in "$@"; do
+  # Check if the file exists
+  if [ ! -f "$file" ]; then
+    echo "Error: File '$file' not found!"
+    continue
+  fi
+
+  # Print 80 dashes
+  printf '%.0s-' {1..80}
+  echo
+
+  # Print the filename and a colon
+  echo "$file:"
+
+  # Print the contents of the file
+  cat "$file"
+
+  # Print a newline for spacing between files
+  echo
+done