+++ /dev/null
-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();
- }
-}
+++ /dev/null
-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();
- }
-}
--- /dev/null
+ //----------------------------------------
+ // 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;
+ }
+
--- /dev/null
+
+On a directed acyclic graph, is it
\ No newline at end of file
--- /dev/null
+# 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.
--- /dev/null
+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
+ );
+ ```
+
--- /dev/null
+# 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.
--- /dev/null
+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);
+ }
+
+
+
+
+}
+++ /dev/null
-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);
- }
-
-}
--- /dev/null
+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);
+ }
+
+
+
+}
--- /dev/null
+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);
+ }
+
+
+
+}
--- /dev/null
+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();
+ }
+}
--- /dev/null
+// LabelList.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList;
+
+public class LabelList extends ArrayList<Label> {
+ // Constructor
+ public LabelList(){
+ super();
+ }
+}
--- /dev/null
+// 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();
+ }
+}
--- /dev/null
+// NodeList.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList;
+
+public class NodeList extends ArrayList<Node> {
+ // Constructor
+ public NodeList(){
+ super();
+ }
+}
--- /dev/null
+// Production.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.function.Function;
+
+public interface Production extends Function<Label, Node> {}
--- /dev/null
+// ProductionList.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.ArrayList;
+
+public class ProductionList extends ArrayList<Production> {
+ // Constructor
+ public ProductionList(){
+ super();
+ }
+}
--- /dev/null
+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();
+ }
+}
--- /dev/null
+// TokenSet.java
+package com.ReasoningTechnology.Ariadne;
+import java.util.HashSet;
+
+public class TokenSet extends HashSet<Token> {
+ // Constructor
+ public TokenSet(){
+ super();
+ }
+}
--- /dev/null
+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(".");
+ }
+
+}
--- /dev/null
+--------------------------------------------------------------------------------
+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>{}
+
-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.
-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.
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{
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 );
--- /dev/null
+#!/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