laser 0.7.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +14 -0
- data/LICENSE +661 -0
- data/README.md +158 -0
- data/Rakefile +104 -0
- data/VERSION +1 -0
- data/bin/laser +7 -0
- data/design_docs/goals.md +57 -0
- data/design_docs/object_regex.md +426 -0
- data/design_docs/type_annotations.md +80 -0
- data/ext/laser/BasicBlock.cpp +572 -0
- data/ext/laser/BasicBlock.h +118 -0
- data/ext/laser/extconf.rb +3 -0
- data/features/laser.feature +25 -0
- data/features/step_definitions/laser_steps.rb +39 -0
- data/features/support/env.rb +14 -0
- data/features/support/testdata/1_input +1 -0
- data/features/support/testdata/1_output +1 -0
- data/features/support/testdata/2_input +4 -0
- data/features/support/testdata/2_output +4 -0
- data/features/support/testdata/3_input +8 -0
- data/features/support/testdata/3_output +11 -0
- data/features/support/testdata/4_input +5 -0
- data/features/support/testdata/4_output +5 -0
- data/features/support/testdata/5_input +13 -0
- data/laser.gemspec +382 -0
- data/lib/laser.rb +98 -0
- data/lib/laser/analysis/annotations.rb +95 -0
- data/lib/laser/analysis/annotations/annotation_config.yaml +3 -0
- data/lib/laser/analysis/annotations/comment_attachment_annotation.rb +66 -0
- data/lib/laser/analysis/annotations/node_pointers_annotation.rb +36 -0
- data/lib/laser/analysis/annotations/runtime_annotation.rb +55 -0
- data/lib/laser/analysis/argument_expansion.rb +132 -0
- data/lib/laser/analysis/arity.rb +34 -0
- data/lib/laser/analysis/bindings.rb +144 -0
- data/lib/laser/analysis/bootstrap/bootstrap.rb +298 -0
- data/lib/laser/analysis/bootstrap/laser_class.rb +106 -0
- data/lib/laser/analysis/bootstrap/laser_method.rb +255 -0
- data/lib/laser/analysis/bootstrap/laser_module.rb +403 -0
- data/lib/laser/analysis/bootstrap/laser_module_copy.rb +74 -0
- data/lib/laser/analysis/bootstrap/laser_object.rb +69 -0
- data/lib/laser/analysis/bootstrap/laser_proc.rb +150 -0
- data/lib/laser/analysis/bootstrap/laser_singleton_class.rb +44 -0
- data/lib/laser/analysis/comments.rb +35 -0
- data/lib/laser/analysis/control_flow.rb +28 -0
- data/lib/laser/analysis/control_flow/alias_analysis.rb +31 -0
- data/lib/laser/analysis/control_flow/basic_block.rb +105 -0
- data/lib/laser/analysis/control_flow/cfg_builder.rb +2505 -0
- data/lib/laser/analysis/control_flow/cfg_instruction.rb +190 -0
- data/lib/laser/analysis/control_flow/constant_propagation.rb +742 -0
- data/lib/laser/analysis/control_flow/control_flow_graph.rb +370 -0
- data/lib/laser/analysis/control_flow/lifetime_analysis.rb +91 -0
- data/lib/laser/analysis/control_flow/method_call_search.rb +26 -0
- data/lib/laser/analysis/control_flow/raise_properties.rb +25 -0
- data/lib/laser/analysis/control_flow/simulation.rb +385 -0
- data/lib/laser/analysis/control_flow/static_single_assignment.rb +185 -0
- data/lib/laser/analysis/control_flow/unreachability_analysis.rb +57 -0
- data/lib/laser/analysis/control_flow/unused_variables.rb +91 -0
- data/lib/laser/analysis/control_flow/yield_properties.rb +103 -0
- data/lib/laser/analysis/errors.rb +131 -0
- data/lib/laser/analysis/laser_utils.rb +18 -0
- data/lib/laser/analysis/lexical_analysis.rb +172 -0
- data/lib/laser/analysis/method_call.rb +68 -0
- data/lib/laser/analysis/protocol_registry.rb +30 -0
- data/lib/laser/analysis/scope.rb +118 -0
- data/lib/laser/analysis/sexp.rb +159 -0
- data/lib/laser/analysis/sexp_analysis.rb +40 -0
- data/lib/laser/analysis/sexp_extensions/constant_extraction.rb +115 -0
- data/lib/laser/analysis/sexp_extensions/source_location.rb +164 -0
- data/lib/laser/analysis/sexp_extensions/type_inference.rb +47 -0
- data/lib/laser/analysis/signature.rb +76 -0
- data/lib/laser/analysis/special_methods/send.rb +67 -0
- data/lib/laser/analysis/unused_methods.rb +21 -0
- data/lib/laser/analysis/visitor.rb +141 -0
- data/lib/laser/annotation_parser/annotations.treetop +126 -0
- data/lib/laser/annotation_parser/annotations_parser.rb +748 -0
- data/lib/laser/annotation_parser/class_annotations.treetop +82 -0
- data/lib/laser/annotation_parser/class_annotations_parser.rb +654 -0
- data/lib/laser/annotation_parser/overload.treetop +24 -0
- data/lib/laser/annotation_parser/overload_parser.rb +167 -0
- data/lib/laser/annotation_parser/parsers.rb +6 -0
- data/lib/laser/annotation_parser/structural.treetop +37 -0
- data/lib/laser/annotation_parser/structural_parser.rb +406 -0
- data/lib/laser/annotation_parser/useful_parsers.treetop +47 -0
- data/lib/laser/annotation_parser/useful_parsers_parser.rb +674 -0
- data/lib/laser/rake/task.rb +46 -0
- data/lib/laser/runner.rb +189 -0
- data/lib/laser/scanner.rb +169 -0
- data/lib/laser/standard_library/_thread.rb +110 -0
- data/lib/laser/standard_library/abbrev.rb +103 -0
- data/lib/laser/standard_library/array.rb +418 -0
- data/lib/laser/standard_library/base64.rb +91 -0
- data/lib/laser/standard_library/basic_object.rb +55 -0
- data/lib/laser/standard_library/benchmark.rb +556 -0
- data/lib/laser/standard_library/bignum.rb +185 -0
- data/lib/laser/standard_library/cgi.rb +275 -0
- data/lib/laser/standard_library/cgi/cookie.rb +147 -0
- data/lib/laser/standard_library/cgi/core.rb +791 -0
- data/lib/laser/standard_library/cgi/html.rb +1021 -0
- data/lib/laser/standard_library/cgi/session.rb +537 -0
- data/lib/laser/standard_library/cgi/session/pstore.rb +111 -0
- data/lib/laser/standard_library/cgi/util.rb +188 -0
- data/lib/laser/standard_library/class_definitions.rb +333 -0
- data/lib/laser/standard_library/comparable.rb +125 -0
- data/lib/laser/standard_library/complex.rb +162 -0
- data/lib/laser/standard_library/enumerable.rb +178 -0
- data/lib/laser/standard_library/exceptions.rb +135 -0
- data/lib/laser/standard_library/fixnum.rb +188 -0
- data/lib/laser/standard_library/float.rb +180 -0
- data/lib/laser/standard_library/hash.rb +237 -0
- data/lib/laser/standard_library/integer.rb +123 -0
- data/lib/laser/standard_library/laser_magic.rb +7 -0
- data/lib/laser/standard_library/nil_false_true.rb +113 -0
- data/lib/laser/standard_library/numbers.rb +192 -0
- data/lib/laser/standard_library/proc.rb +31 -0
- data/lib/laser/standard_library/set.rb +1348 -0
- data/lib/laser/standard_library/string.rb +666 -0
- data/lib/laser/standard_library/stringio.rb +2 -0
- data/lib/laser/standard_library/symbol.rb +125 -0
- data/lib/laser/standard_library/tsort.rb +242 -0
- data/lib/laser/support/acts_as_struct.rb +66 -0
- data/lib/laser/support/frequency.rb +55 -0
- data/lib/laser/support/inheritable_attributes.rb +145 -0
- data/lib/laser/support/module_extensions.rb +94 -0
- data/lib/laser/support/placeholder_object.rb +13 -0
- data/lib/laser/third_party/rgl/adjacency.rb +221 -0
- data/lib/laser/third_party/rgl/base.rb +228 -0
- data/lib/laser/third_party/rgl/bidirectional.rb +39 -0
- data/lib/laser/third_party/rgl/condensation.rb +47 -0
- data/lib/laser/third_party/rgl/connected_components.rb +138 -0
- data/lib/laser/third_party/rgl/control_flow.rb +170 -0
- data/lib/laser/third_party/rgl/depth_first_spanning_tree.rb +37 -0
- data/lib/laser/third_party/rgl/dominators.rb +124 -0
- data/lib/laser/third_party/rgl/dot.rb +93 -0
- data/lib/laser/third_party/rgl/graphxml.rb +51 -0
- data/lib/laser/third_party/rgl/implicit.rb +174 -0
- data/lib/laser/third_party/rgl/mutable.rb +117 -0
- data/lib/laser/third_party/rgl/rdot.rb +445 -0
- data/lib/laser/third_party/rgl/topsort.rb +72 -0
- data/lib/laser/third_party/rgl/transitivity.rb +180 -0
- data/lib/laser/third_party/rgl/traversal.rb +348 -0
- data/lib/laser/types/types.rb +433 -0
- data/lib/laser/version.rb +14 -0
- data/lib/laser/warning.rb +149 -0
- data/lib/laser/warning_sets/default.yml +13 -0
- data/lib/laser/warnings/assignment_in_condition.rb +20 -0
- data/lib/laser/warnings/comment_spacing.rb +31 -0
- data/lib/laser/warnings/extra_blank_lines.rb +30 -0
- data/lib/laser/warnings/extra_whitespace.rb +16 -0
- data/lib/laser/warnings/hash_symbol_18_warning.rb +63 -0
- data/lib/laser/warnings/hash_symbol_19_warning.rb +29 -0
- data/lib/laser/warnings/line_length.rb +115 -0
- data/lib/laser/warnings/misaligned_unindentation.rb +17 -0
- data/lib/laser/warnings/operator_spacing.rb +68 -0
- data/lib/laser/warnings/parens_on_declaration.rb +30 -0
- data/lib/laser/warnings/rescue_exception.rb +42 -0
- data/lib/laser/warnings/semicolon.rb +25 -0
- data/lib/laser/warnings/sexp_errors.rb +24 -0
- data/lib/laser/warnings/uncalled_method_warning.rb +7 -0
- data/lib/laser/warnings/useless_double_quotes.rb +38 -0
- data/spec/analysis_specs/annotations_spec.rb +47 -0
- data/spec/analysis_specs/annotations_specs/comment_attachment_spec.rb +68 -0
- data/spec/analysis_specs/annotations_specs/node_pointers_annotation_spec.rb +90 -0
- data/spec/analysis_specs/annotations_specs/runtime_annotation_spec.rb +135 -0
- data/spec/analysis_specs/annotations_specs/spec_helper.rb +33 -0
- data/spec/analysis_specs/argument_expansion_spec.rb +113 -0
- data/spec/analysis_specs/bindings_spec.rb +36 -0
- data/spec/analysis_specs/comment_spec.rb +93 -0
- data/spec/analysis_specs/control_flow_specs/cfg_instruction_spec.rb +111 -0
- data/spec/analysis_specs/control_flow_specs/constant_propagation_spec.rb +560 -0
- data/spec/analysis_specs/control_flow_specs/control_flow_graph_spec.rb +5 -0
- data/spec/analysis_specs/control_flow_specs/raise_properties_spec.rb +310 -0
- data/spec/analysis_specs/control_flow_specs/raise_type_inference_spec.rb +301 -0
- data/spec/analysis_specs/control_flow_specs/return_type_inference_spec.rb +431 -0
- data/spec/analysis_specs/control_flow_specs/simulation_spec.rb +158 -0
- data/spec/analysis_specs/control_flow_specs/spec_helper.rb +110 -0
- data/spec/analysis_specs/control_flow_specs/tuple_misuse_inference_spec.rb +125 -0
- data/spec/analysis_specs/control_flow_specs/unreachability_analysis_spec.rb +76 -0
- data/spec/analysis_specs/control_flow_specs/unused_variable_spec.rb +99 -0
- data/spec/analysis_specs/control_flow_specs/yield_properties_spec.rb +372 -0
- data/spec/analysis_specs/error_spec.rb +30 -0
- data/spec/analysis_specs/laser_class_spec.rb +322 -0
- data/spec/analysis_specs/lexical_analysis_spec.rb +184 -0
- data/spec/analysis_specs/protocol_registry_spec.rb +63 -0
- data/spec/analysis_specs/scope_annotation_spec.rb +1013 -0
- data/spec/analysis_specs/scope_spec.rb +126 -0
- data/spec/analysis_specs/sexp_analysis_spec.rb +30 -0
- data/spec/analysis_specs/sexp_extension_specs/constant_extraction_spec.rb +309 -0
- data/spec/analysis_specs/sexp_extension_specs/source_location_spec.rb +231 -0
- data/spec/analysis_specs/sexp_extension_specs/spec_helper.rb +1 -0
- data/spec/analysis_specs/sexp_extension_specs/type_inference_spec.rb +252 -0
- data/spec/analysis_specs/sexp_spec.rb +167 -0
- data/spec/analysis_specs/spec_helper.rb +27 -0
- data/spec/analysis_specs/unused_methods_spec.rb +65 -0
- data/spec/analysis_specs/visitor_spec.rb +64 -0
- data/spec/annotation_parser_specs/annotations_parser_spec.rb +89 -0
- data/spec/annotation_parser_specs/class_annotation_parser_spec.rb +120 -0
- data/spec/annotation_parser_specs/overload_parser_spec.rb +39 -0
- data/spec/annotation_parser_specs/parsers_spec.rb +14 -0
- data/spec/annotation_parser_specs/spec_helper.rb +1 -0
- data/spec/annotation_parser_specs/structural_parser_spec.rb +67 -0
- data/spec/laser_spec.rb +14 -0
- data/spec/rake_specs/spec_helper.rb +1 -0
- data/spec/rake_specs/task_spec.rb +67 -0
- data/spec/runner_spec.rb +207 -0
- data/spec/scanner_spec.rb +75 -0
- data/spec/spec_helper.rb +121 -0
- data/spec/standard_library/exceptions_spec.rb +19 -0
- data/spec/standard_library/globals_spec.rb +14 -0
- data/spec/standard_library/set_spec.rb +31 -0
- data/spec/standard_library/spec_helper.rb +1 -0
- data/spec/standard_library/standard_library_spec.rb +302 -0
- data/spec/support_specs/acts_as_struct_spec.rb +94 -0
- data/spec/support_specs/frequency_spec.rb +23 -0
- data/spec/support_specs/module_extensions_spec.rb +117 -0
- data/spec/support_specs/spec_helper.rb +1 -0
- data/spec/type_specs/spec_helper.rb +1 -0
- data/spec/type_specs/types_spec.rb +133 -0
- data/spec/warning_spec.rb +95 -0
- data/spec/warning_specs/assignment_in_condition_spec.rb +68 -0
- data/spec/warning_specs/comment_spacing_spec.rb +65 -0
- data/spec/warning_specs/extra_blank_lines_spec.rb +70 -0
- data/spec/warning_specs/extra_whitespace_spec.rb +33 -0
- data/spec/warning_specs/hash_symbol_18_warning_spec.rb +89 -0
- data/spec/warning_specs/hash_symbol_19_warning_spec.rb +63 -0
- data/spec/warning_specs/line_length_spec.rb +173 -0
- data/spec/warning_specs/misaligned_unindentation_spec.rb +35 -0
- data/spec/warning_specs/operator_spacing_spec.rb +104 -0
- data/spec/warning_specs/parens_on_declaration_spec.rb +57 -0
- data/spec/warning_specs/rescue_exception_spec.rb +105 -0
- data/spec/warning_specs/semicolon_spec.rb +58 -0
- data/spec/warning_specs/spec_helper.rb +1 -0
- data/spec/warning_specs/useless_double_quotes_spec.rb +74 -0
- data/status_reports/2010/12/2010-12-14.md +163 -0
- data/status_reports/2010/12/2010-12-23.md +298 -0
- data/status_reports/2010/12/2010-12-24.md +6 -0
- data/test/third_party_tests/rgl_tests/TestComponents.rb +65 -0
- data/test/third_party_tests/rgl_tests/TestCycles.rb +61 -0
- data/test/third_party_tests/rgl_tests/TestDirectedGraph.rb +125 -0
- data/test/third_party_tests/rgl_tests/TestDot.rb +18 -0
- data/test/third_party_tests/rgl_tests/TestEdge.rb +34 -0
- data/test/third_party_tests/rgl_tests/TestGraph.rb +71 -0
- data/test/third_party_tests/rgl_tests/TestGraphXML.rb +57 -0
- data/test/third_party_tests/rgl_tests/TestImplicit.rb +52 -0
- data/test/third_party_tests/rgl_tests/TestRdot.rb +863 -0
- data/test/third_party_tests/rgl_tests/TestTransitivity.rb +129 -0
- data/test/third_party_tests/rgl_tests/TestTraversal.rb +220 -0
- data/test/third_party_tests/rgl_tests/TestUnDirectedGraph.rb +102 -0
- data/test/third_party_tests/rgl_tests/examples/north/Graph.log +128 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.0.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.1.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.11.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.12.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.13.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.14.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.15.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.16.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.17.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.19.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.2.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.20.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.22.graphml +43 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.24.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.25.graphml +45 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.27.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.28.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.29.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.3.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.30.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.31.graphml +42 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.34.graphml +42 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.37.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.38.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.39.graphml +36 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.4.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.40.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.41.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.42.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.45.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.46.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.5.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.50.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.56.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.57.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.58.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.6.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.60.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.61.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.62.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.68.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.69.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.7.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.70.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.71.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.72.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.74.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.75.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.78.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.79.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.8.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.80.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.82.graphml +35 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.83.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.85.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.86.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.88.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.89.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.9.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.90.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.91.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.92.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.93.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.94.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.12.8.graphml +40 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.14.9.graphml +36 -0
- data/test/third_party_tests/rgl_tests/test_helper.rb +7 -0
- data/test/third_party_tests/test_inheritable_attributes.rb +187 -0
- metadata +470 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# implicit.rb
|
|
2
|
+
#
|
|
3
|
+
# This file contains the definition of the class RGL::ImplicitGraph, which
|
|
4
|
+
# defines vertex and edge iterators using blocks (which again call blocks).
|
|
5
|
+
#
|
|
6
|
+
# An ImplicitGraph provides a handy way to define graphs on the fly, using two
|
|
7
|
+
# blocks for the two iterators defining a graph. A directed cyclic graph,
|
|
8
|
+
# with five vertices can be created as follows:
|
|
9
|
+
#
|
|
10
|
+
# g = RGL::ImplicitGraph.new { |g|
|
|
11
|
+
# g.vertex_iterator { |b| 0.upto(4,&b) }
|
|
12
|
+
# g.adjacent_iterator { |x, b| b.call((x+1)%5) }
|
|
13
|
+
# g.directed = true
|
|
14
|
+
# }
|
|
15
|
+
#
|
|
16
|
+
# g.to_s => "(0-1)(1-2)(2-3)(3-4)(4-0)"
|
|
17
|
+
#
|
|
18
|
+
# Other examples are given by the methods vertices_filtered_by and
|
|
19
|
+
# edges_filtered_by, which can be applied to any graph.
|
|
20
|
+
|
|
21
|
+
require 'laser/third_party/rgl/base'
|
|
22
|
+
|
|
23
|
+
module RGL
|
|
24
|
+
|
|
25
|
+
class ImplicitGraph
|
|
26
|
+
|
|
27
|
+
include Graph
|
|
28
|
+
|
|
29
|
+
attr_writer :directed
|
|
30
|
+
|
|
31
|
+
EMPTY_VERTEX_ITERATOR = proc { |b| }
|
|
32
|
+
EMPTY_NEIGHBOR_ITERATOR = proc { |x, b| }
|
|
33
|
+
|
|
34
|
+
# Create a new ImplicitGraph, which is empty by default. The caller should
|
|
35
|
+
# configure the graph using vertex and neighbor iterators. If the graph is
|
|
36
|
+
# directed, the client should set _directed_ to true. The default value
|
|
37
|
+
# for _directed_ is false.
|
|
38
|
+
|
|
39
|
+
def initialize
|
|
40
|
+
@directed = false
|
|
41
|
+
@vertex_iterator = EMPTY_VERTEX_ITERATOR
|
|
42
|
+
@adjacent_iterator = EMPTY_NEIGHBOR_ITERATOR
|
|
43
|
+
yield self if block_given? # Let client overwrite defaults.
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns the value of @directed.
|
|
47
|
+
|
|
48
|
+
def directed?
|
|
49
|
+
@directed
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def each_vertex (&block) # :nodoc:
|
|
53
|
+
@vertex_iterator.call(block)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def each_adjacent (v, &block) # :nodoc:
|
|
57
|
+
@adjacent_iterator.call(v, block)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def each_edge (&block) # :nodoc:
|
|
61
|
+
if defined? @edge_iterator
|
|
62
|
+
@edge_iterator.call(block)
|
|
63
|
+
else
|
|
64
|
+
super # use default implementation
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Sets the vertex_iterator to _block_,
|
|
69
|
+
# which must be a block of one parameter
|
|
70
|
+
# which again is the block called by each_vertex.
|
|
71
|
+
|
|
72
|
+
def vertex_iterator (&block)
|
|
73
|
+
@vertex_iterator = block
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Sets the adjacent_iterator to _block_,
|
|
77
|
+
# which must be a block of two parameters:
|
|
78
|
+
#
|
|
79
|
+
# The first parameter is the vertex the neighbors of which are to be
|
|
80
|
+
# traversed.
|
|
81
|
+
#
|
|
82
|
+
# The second is the block which will be called for each neighbor
|
|
83
|
+
# of this vertex.
|
|
84
|
+
|
|
85
|
+
def adjacent_iterator (&block)
|
|
86
|
+
@adjacent_iterator = block
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Sets the edge_iterator to _block_, which must be a block of two
|
|
90
|
+
# parameters: The first parameter is the source of the edges; the
|
|
91
|
+
# second is the target of the edge.
|
|
92
|
+
|
|
93
|
+
def edge_iterator (&block)
|
|
94
|
+
@edge_iterator = block
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end # class ImplicitGraph
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
module Graph
|
|
101
|
+
|
|
102
|
+
# ---
|
|
103
|
+
# === Graph adaptors
|
|
104
|
+
#
|
|
105
|
+
# Return a new ImplicitGraph which has as vertices all vertices of the
|
|
106
|
+
# receiver which satisfy the predicate _filter_.
|
|
107
|
+
#
|
|
108
|
+
# The methods provides similar functionaty as the BGL graph adapter
|
|
109
|
+
# filtered_graph (see BOOST_DOC/filtered_graph.html).
|
|
110
|
+
#
|
|
111
|
+
# ==== Example
|
|
112
|
+
#
|
|
113
|
+
# def complete (n)
|
|
114
|
+
# set = n.integer? ? (1..n) : n
|
|
115
|
+
# RGL::ImplicitGraph.new { |g|
|
|
116
|
+
# g.vertex_iterator { |b| set.each(&b) }
|
|
117
|
+
# g.adjacent_iterator { |x, b|
|
|
118
|
+
# set.each { |y| b.call(y) unless x == y }
|
|
119
|
+
# }
|
|
120
|
+
# }
|
|
121
|
+
# end
|
|
122
|
+
#
|
|
123
|
+
# complete(4).to_s => "(1=2)(1=3)(1=4)(2=3)(2=4)(3=4)"
|
|
124
|
+
# complete(4).vertices_filtered_by {|v| v != 4}.to_s => "(1=2)(1=3)(2=3)"
|
|
125
|
+
|
|
126
|
+
def vertices_filtered_by (&filter)
|
|
127
|
+
implicit_graph { |g|
|
|
128
|
+
g.vertex_iterator { |b|
|
|
129
|
+
self.each_vertex { |v| b.call(v) if filter.call(v) }
|
|
130
|
+
}
|
|
131
|
+
g.adjacent_iterator { |v, b|
|
|
132
|
+
self.each_adjacent(v) { |u| b.call(u) if filter.call(u) }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Return a new ImplicitGraph which has as edges all edges of the receiver
|
|
138
|
+
# which satisfy the predicate _filter_ (a block with two parameters).
|
|
139
|
+
#
|
|
140
|
+
# ==== Example
|
|
141
|
+
#
|
|
142
|
+
# g = complete(7).edges_filtered_by {|u,v| u+v == 7}
|
|
143
|
+
# g.to_s => "(1=6)(2=5)(3=4)"
|
|
144
|
+
# g.vertices => [1, 2, 3, 4, 5, 6, 7]
|
|
145
|
+
|
|
146
|
+
def edges_filtered_by (&filter)
|
|
147
|
+
implicit_graph { |g|
|
|
148
|
+
g.adjacent_iterator { |v, b|
|
|
149
|
+
self.each_adjacent(v) { |u|
|
|
150
|
+
b.call(u) if filter.call(v, u)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
g.edge_iterator { |b|
|
|
154
|
+
self.each_edge { |u,v| b.call(u, v) if filter.call(u, v) }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Return a new ImplicitGraph which is isomorphic (i.e. has same edges and
|
|
160
|
+
# vertices) to the receiver. It is a shortcut, also used by
|
|
161
|
+
# edges_filtered_by and vertices_filtered_by.
|
|
162
|
+
|
|
163
|
+
def implicit_graph
|
|
164
|
+
result = ImplicitGraph.new { |g|
|
|
165
|
+
g.vertex_iterator { |b| self.each_vertex(&b) }
|
|
166
|
+
g.adjacent_iterator { |v, b| self.each_adjacent(v, &b) }
|
|
167
|
+
g.directed = self.directed?
|
|
168
|
+
}
|
|
169
|
+
yield result if block_given? # let client overwrite defaults
|
|
170
|
+
result
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
end # module Graph
|
|
174
|
+
end # module RGL
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# mutable.rb
|
|
2
|
+
|
|
3
|
+
require 'laser/third_party/rgl/base'
|
|
4
|
+
|
|
5
|
+
module RGL
|
|
6
|
+
|
|
7
|
+
# A MutableGraph can be changed via the addition or removal of edges and
|
|
8
|
+
# vertices.
|
|
9
|
+
module MutableGraph
|
|
10
|
+
|
|
11
|
+
include Graph
|
|
12
|
+
|
|
13
|
+
# Add a new vertex _v_ to the graph. If the vertex is already in the
|
|
14
|
+
# graph (tested via eql?), the method does nothing.
|
|
15
|
+
|
|
16
|
+
def add_vertex (v)
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Inserts the edge (u,v) into the graph.
|
|
21
|
+
#
|
|
22
|
+
# Note that for undirected graphs, (u,v) is the same edge as (v,u), so
|
|
23
|
+
# after a call to the function add_edge(), this implies that edge (u,v)
|
|
24
|
+
# will appear in the out-edges of u and (u,v) (or equivalently (v,u))
|
|
25
|
+
# will appear in the out-edges of v. Put another way, v will be adjacent
|
|
26
|
+
# to u and u will be adjacent to v.
|
|
27
|
+
|
|
28
|
+
def add_edge (u, v)
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Add all objects in _a_ to the vertex set.
|
|
33
|
+
|
|
34
|
+
def add_vertices (*a)
|
|
35
|
+
a.each { |v| add_vertex v }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Add all edges in the _edges_ array to the edge set. Elements of the
|
|
39
|
+
# array can be both two-element arrays or instances of DirectedEdge or
|
|
40
|
+
# UnDirectedEdge.
|
|
41
|
+
|
|
42
|
+
def add_edges (*edges)
|
|
43
|
+
edges.each { |edge| add_edge(edge[0], edge[1]) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Add the graph to the current graph. All vertices in the other graph
|
|
47
|
+
# are added, as are all edges.
|
|
48
|
+
|
|
49
|
+
def add_graph(graph)
|
|
50
|
+
add_vertices(*graph.vertices)
|
|
51
|
+
add_edges(*graph.edges)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Adds all the given graphs to the current graph. See #add_graph.
|
|
55
|
+
|
|
56
|
+
def add_graphs(*graphs)
|
|
57
|
+
graphs.each { |graph| add_graph graph }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Remove u from the vertex set of the graph. All edges whose target is
|
|
61
|
+
# _v_ are also removed from the edge set of the graph.
|
|
62
|
+
#
|
|
63
|
+
# Postcondition: num_vertices is one less, _v_ no longer appears in the
|
|
64
|
+
# vertex set of the graph, and there no edge with source or target _v_.
|
|
65
|
+
|
|
66
|
+
def remove_vertex (v)
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Remove the edge (u,v) from the graph. If the graph allows parallel
|
|
71
|
+
# edges, this removes all occurrences of (u,v).
|
|
72
|
+
#
|
|
73
|
+
# Precondition: u and v are vertices in the graph.
|
|
74
|
+
# Postcondition: (u,v) is no longer in the edge set for g.
|
|
75
|
+
|
|
76
|
+
def remove_edge (u, v)
|
|
77
|
+
raise NotImplementedError
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Remove all vertices specified by the array a from the graph by calling
|
|
81
|
+
# remove_vertex.
|
|
82
|
+
|
|
83
|
+
def remove_vertices (*a)
|
|
84
|
+
a.each { |v| remove_vertex v }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns all minimum cycles that pass through a give vertex.
|
|
88
|
+
# The format is an Array of cycles, with each cycle being an Array
|
|
89
|
+
# of vertices in the cycle.
|
|
90
|
+
def cycles_with_vertex(vertex)
|
|
91
|
+
cycles_with_vertex_helper(vertex, vertex, [])
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
protected
|
|
95
|
+
def cycles_with_vertex_helper(vertex, start, visited) #:nodoc:
|
|
96
|
+
adjacent_vertices(start).reject {|x| visited.include?(x)}.inject([]) do |acc, adj|
|
|
97
|
+
local_visited = Array.new(visited) << adj
|
|
98
|
+
acc << local_visited if (adj==vertex)
|
|
99
|
+
acc = acc + cycles_with_vertex_helper(vertex,adj,local_visited)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
public
|
|
104
|
+
# Returns an array of all minimum cycles in a graph
|
|
105
|
+
#
|
|
106
|
+
# This is not an efficient implementation O(n^4) and could
|
|
107
|
+
# be done using Minimum Spanning Trees. Hint. Hint.
|
|
108
|
+
def cycles
|
|
109
|
+
g = self.clone
|
|
110
|
+
self.inject([]) do |acc, v|
|
|
111
|
+
acc = acc.concat(g.cycles_with_vertex(v))
|
|
112
|
+
g.remove_vertex(v); acc
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end # module MutableGraph
|
|
117
|
+
end # module RGL
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# This is a modified version of dot.rb from Dave Thomas's rdoc project. I
|
|
2
|
+
# renamed it to rdot.rb to avoid collision with an installed rdoc/dot.
|
|
3
|
+
#
|
|
4
|
+
# It also supports undirected edges.
|
|
5
|
+
|
|
6
|
+
module RGL; module DOT
|
|
7
|
+
|
|
8
|
+
# options for node declaration
|
|
9
|
+
|
|
10
|
+
NODE_OPTS = [
|
|
11
|
+
# attributes due to
|
|
12
|
+
# http://www.graphviz.org/Documentation/dotguide.pdf
|
|
13
|
+
# February 23, 2008
|
|
14
|
+
'color', # default: black; node shape color
|
|
15
|
+
'comment', # any string (format-dependent)
|
|
16
|
+
'distortion', # default: 0.0; node distortion for shape=polygon
|
|
17
|
+
'fillcolor', # default: lightgrey/black; node fill color
|
|
18
|
+
'fixedsize', # default: false; label text has no affect on node size
|
|
19
|
+
'fontcolor', # default: black; type face color
|
|
20
|
+
'fontname', # default: Times-Roman; font family
|
|
21
|
+
'fontsize', #default: 14; point size of label
|
|
22
|
+
'group', # name of node's group
|
|
23
|
+
'height', # default: .5; height in inches
|
|
24
|
+
'label', # default: node name; any string
|
|
25
|
+
'layer', # default: overlay range; all, id or id:id
|
|
26
|
+
'orientation', # dafault: 0.0; node rotation angle
|
|
27
|
+
'peripheries', # shape-dependent number of node boundaries
|
|
28
|
+
'regular', # default: false; force polygon to be regular
|
|
29
|
+
'shape', # default: ellipse; node shape; see Section 2.1 and Appendix E
|
|
30
|
+
'shapefile', # external EPSF or SVG custom shape file
|
|
31
|
+
'sides', # default: 4; number of sides for shape=polygon
|
|
32
|
+
'skew' , # default: 0.0; skewing of node for shape=polygon
|
|
33
|
+
'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3
|
|
34
|
+
'URL', # URL associated with node (format-dependent)
|
|
35
|
+
'width', # default: .75; width in inches
|
|
36
|
+
'z', #default: 0.0; z coordinate for VRML output
|
|
37
|
+
|
|
38
|
+
# maintained for backward compatibility or rdot internal
|
|
39
|
+
'bottomlabel', # auxiliary label for nodes of shape M*
|
|
40
|
+
'bgcolor',
|
|
41
|
+
'rank',
|
|
42
|
+
'toplabel' # auxiliary label for nodes of shape M*
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
# options for edge declaration
|
|
46
|
+
|
|
47
|
+
EDGE_OPTS = [
|
|
48
|
+
'arrowhead', # default: normal; style of arrowhead at head end
|
|
49
|
+
'arrowsize', # default: 1.0; scaling factor for arrowheads
|
|
50
|
+
'arrowtail', # default: normal; style of arrowhead at tail end
|
|
51
|
+
'color', # default: black; edge stroke color
|
|
52
|
+
'comment', # any string (format-dependent)
|
|
53
|
+
'constraint', # default: true use edge to affect node ranking
|
|
54
|
+
'decorate', # if set, draws a line connecting labels with their edges
|
|
55
|
+
'dir', # default: forward; forward, back, both, or none
|
|
56
|
+
'fontcolor', # default: black type face color
|
|
57
|
+
'fontname', # default: Times-Roman; font family
|
|
58
|
+
'fontsize', # default: 14; point size of label
|
|
59
|
+
'headlabel', # label placed near head of edge
|
|
60
|
+
'headport', # n,ne,e,se,s,sw,w,nw
|
|
61
|
+
'headURL', # URL attached to head label if output format is ismap
|
|
62
|
+
'label', # edge label
|
|
63
|
+
'labelangle', # default: -25.0; angle in degrees which head or tail label is rotated off edge
|
|
64
|
+
'labeldistance', # default: 1.0; scaling factor for distance of head or tail label from node
|
|
65
|
+
'labelfloat', # default: false; lessen constraints on edge label placement
|
|
66
|
+
'labelfontcolor', # default: black; type face color for head and tail labels
|
|
67
|
+
'labelfontname', # default: Times-Roman; font family for head and tail labels
|
|
68
|
+
'labelfontsize', # default: 14 point size for head and tail labels
|
|
69
|
+
'layer', # default: overlay range; all, id or id:id
|
|
70
|
+
'lhead', # name of cluster to use as head of edge
|
|
71
|
+
'ltail', # name of cluster to use as tail of edge
|
|
72
|
+
'minlen', # default: 1 minimum rank distance between head and tail
|
|
73
|
+
'samehead', # tag for head node; edge heads with the same tag are merged onto the same port
|
|
74
|
+
'sametail', # tag for tail node; edge tails with the same tag are merged onto the same port
|
|
75
|
+
'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3
|
|
76
|
+
'taillabel', # label placed near tail of edge
|
|
77
|
+
'tailport', # n,ne,e,se,s,sw,w,nw
|
|
78
|
+
'tailURL', # URL attached to tail label if output format is ismap
|
|
79
|
+
'weight', # default: 1; integer cost of stretching an edge
|
|
80
|
+
|
|
81
|
+
# maintained for backward compatibility or rdot internal
|
|
82
|
+
'id'
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
# options for graph declaration
|
|
86
|
+
|
|
87
|
+
GRAPH_OPTS = [
|
|
88
|
+
'bgcolor', # background color for drawing, plus initial fill color
|
|
89
|
+
'center', # default: false; center draing on page
|
|
90
|
+
'clusterrank', # default: local; may be "global" or "none"
|
|
91
|
+
'color', # default: black; for clusters, outline color, and fill color if
|
|
92
|
+
# fillcolor not defined
|
|
93
|
+
'comment', # any string (format-dependent)
|
|
94
|
+
'compound', # default: false; allow edges between clusters
|
|
95
|
+
'concentrate', # default: false; enables edge concentrators
|
|
96
|
+
'fillcolor', # default: black; cluster fill color
|
|
97
|
+
'fontcolor', # default: black; type face color
|
|
98
|
+
'fontname', # default: Times-Roman; font family
|
|
99
|
+
'fontpath', # list of directories to search for fonts
|
|
100
|
+
'fontsize', # default: 14; point size of label
|
|
101
|
+
'label', # any string
|
|
102
|
+
'labeljust', # default: centered; "l" and "r" for left- and right-justified
|
|
103
|
+
# cluster labels, respectively
|
|
104
|
+
'labelloc', # default: top; "t" and "b" for top- and bottom-justified
|
|
105
|
+
# cluster labels, respectively
|
|
106
|
+
'layers', # id:id:id...
|
|
107
|
+
'margin', # default: .5; margin included in page, inches
|
|
108
|
+
'mclimit', # default: 1.0; scale factor for mincross iterations
|
|
109
|
+
'nodesep', # default: .25; separation between nodes, in inches.
|
|
110
|
+
'nslimit', # if set to "f", bounds network simplex iterations by
|
|
111
|
+
# (f)(number of nodes) when setting x-coordinates
|
|
112
|
+
'nslimit1', # if set to "f", bounds network simplex iterations by
|
|
113
|
+
# (f)(number of nodes) when ranking nodes
|
|
114
|
+
'ordering', # if "out" out edge order is preserved
|
|
115
|
+
'orientation', # default: portrait; if "rotate" is not used and the value is
|
|
116
|
+
# "landscape", use landscape orientation
|
|
117
|
+
'page', # unit of pagination, e.g. "8.5,11"
|
|
118
|
+
'rank', # "same", "min", "max", "source", or "sink"
|
|
119
|
+
'rankdir', # default: TB; "LR" (left to right) or "TB" (top to bottom)
|
|
120
|
+
'ranksep', # default: .75; separation between ranks, in inches.
|
|
121
|
+
'ratio', # approximate aspect ratio desired, "fill" or "auto"
|
|
122
|
+
'samplepoints', # default: 8; number of points used to represent ellipses
|
|
123
|
+
# and circles on output
|
|
124
|
+
'searchsize', # default: 30; maximum edges with negative cut values to check
|
|
125
|
+
# when looking for a minimum one during network simplex
|
|
126
|
+
'size', # maximum drawing size, in inches
|
|
127
|
+
'style', # graphics options, e.g. "filled" for clusters
|
|
128
|
+
'URL', # URL associated with graph (format-dependent)
|
|
129
|
+
|
|
130
|
+
# maintained for backward compatibility or rdot internal
|
|
131
|
+
'layerseq'
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
# Ancestor of Edge, Node, and Graph.
|
|
135
|
+
class Element
|
|
136
|
+
attr_accessor :name, :options
|
|
137
|
+
|
|
138
|
+
def initialize (params = {}, option_list = []) # :nodoc:
|
|
139
|
+
@name = params['name'] ? params['name'] : nil
|
|
140
|
+
@options = {}
|
|
141
|
+
option_list.each{ |i|
|
|
142
|
+
@options[i] = params[i] if params[i]
|
|
143
|
+
}
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
# Returns the string given in _id_ within quotes if necessary. Special
|
|
148
|
+
# characters are escaped as necessary.
|
|
149
|
+
def quote_ID(id)
|
|
150
|
+
# Ensure that the ID is a string.
|
|
151
|
+
id = id.to_s
|
|
152
|
+
|
|
153
|
+
# Return the ID verbatim if it looks like a name, a number, or HTML.
|
|
154
|
+
return id if id =~ /\A([[:alpha:]_][[:alnum:]_]*|-?(\.[[:digit:]]+|[[:digit:]]+(\.[[:digit:]]*)?)|<.*>)\Z/m and id[-1] != ?\n
|
|
155
|
+
|
|
156
|
+
# Return a quoted version of the ID otherwise.
|
|
157
|
+
'"' + id.gsub('\\', '\\\\\\\\').gsub('"', '\\\\"') + '"'
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Returns the string given in _label_ within quotes if necessary. Special
|
|
161
|
+
# characters are escaped as necessary. Labels get special treatment in
|
|
162
|
+
# order to handle embedded *\n*, *\r*, and *\l* sequences which are copied
|
|
163
|
+
# into the new string verbatim.
|
|
164
|
+
def quote_label(label)
|
|
165
|
+
# Ensure that the label is a string.
|
|
166
|
+
label = label.to_s
|
|
167
|
+
|
|
168
|
+
# Return the label verbatim if it looks like a name, a number, or HTML.
|
|
169
|
+
return label if label =~ /\A([[:alpha:]_][[:alnum:]_]*|-?(\.[[:digit:]]+|[[:digit:]]+(\.[[:digit:]]*)?)|<.*>)\Z/m and label[-1] != ?\n
|
|
170
|
+
|
|
171
|
+
# Return a quoted version of the label otherwise.
|
|
172
|
+
'"' + label.split(/(\\n|\\r|\\l)/).collect do |part|
|
|
173
|
+
case part
|
|
174
|
+
when "\\n", "\\r", "\\l"
|
|
175
|
+
part
|
|
176
|
+
else
|
|
177
|
+
part.gsub('\\', '\\\\\\\\').gsub('"', '\\\\"').gsub("\n", '\\n')
|
|
178
|
+
end
|
|
179
|
+
end.join + '"'
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Ports are used when a Node instance has its `shape' option set to
|
|
185
|
+
# _record_ or _Mrecord_. Ports can be nested.
|
|
186
|
+
class Port
|
|
187
|
+
attr_accessor :name, :label, :ports
|
|
188
|
+
|
|
189
|
+
# Create a new port with either an optional name and label or a set of
|
|
190
|
+
# nested ports.
|
|
191
|
+
#
|
|
192
|
+
# :call-seq:
|
|
193
|
+
# new(name = nil, label = nil)
|
|
194
|
+
# new(ports)
|
|
195
|
+
#
|
|
196
|
+
# A +nil+ value for +name+ is valid; otherwise, it must be a String or it
|
|
197
|
+
# will be interpreted as +ports+.
|
|
198
|
+
def initialize (name_or_ports = nil, label = nil)
|
|
199
|
+
if name_or_ports.nil? or name_or_ports.kind_of?(String) then
|
|
200
|
+
@name = name_or_ports
|
|
201
|
+
@label = label
|
|
202
|
+
@ports = nil
|
|
203
|
+
else
|
|
204
|
+
@ports = name_or_ports
|
|
205
|
+
@name = nil
|
|
206
|
+
@label = nil
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Returns a string representation of this port. If ports is a non-empty
|
|
211
|
+
# Enumerable, a nested ports representation is returned; otherwise, a
|
|
212
|
+
# name-label representation is returned.
|
|
213
|
+
def to_s
|
|
214
|
+
if @ports.nil? or @ports.empty? then
|
|
215
|
+
n = (name.nil? or name.empty?) ? '' : "<#{name}>"
|
|
216
|
+
n + ((n.empty? or label.nil? or label.empty?) ? '' : ' ') + label.to_s
|
|
217
|
+
else
|
|
218
|
+
'{' + @ports.collect {|p| p.to_s}.join(' | ') + '}'
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# A node representation. Edges are drawn between nodes. The rendering of a
|
|
224
|
+
# node depends upon the options set for it.
|
|
225
|
+
class Node < Element
|
|
226
|
+
attr_accessor :ports
|
|
227
|
+
|
|
228
|
+
# Creates a new Node with the _params_ Hash providing settings for all
|
|
229
|
+
# node options. The _option_list_ parameter restricts those options to the
|
|
230
|
+
# list of valid names it contains. The exception to this is the _ports_
|
|
231
|
+
# option which, if specified, must be an Enumerable containing a list of
|
|
232
|
+
# ports.
|
|
233
|
+
def initialize (params = {}, option_list = NODE_OPTS)
|
|
234
|
+
super(params, option_list)
|
|
235
|
+
@ports = params['ports'] ? params['ports'] : []
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Returns a string representation of this node which is consumable by the
|
|
239
|
+
# graphviz tools +dot+ and +neato+. The _leader_ parameter is used to indent
|
|
240
|
+
# every line of the returned string, and the _indent_ parameter is used to
|
|
241
|
+
# additionally indent nested items.
|
|
242
|
+
def to_s (leader = '', indent = ' ')
|
|
243
|
+
label_option = nil
|
|
244
|
+
if @options['shape'] =~ /^M?record$/ && !@ports.empty? then
|
|
245
|
+
# Ignore the given label option in this case since the ports should each
|
|
246
|
+
# provide their own name/label.
|
|
247
|
+
label_option = leader + indent + "#{quote_ID('label')} = #{quote_ID(@ports.collect { |port| port.to_s }.join(" | "))}"
|
|
248
|
+
elsif @options['label'] then
|
|
249
|
+
# Otherwise, use the label when given one.
|
|
250
|
+
label_option = leader + indent + "#{quote_ID('label')} = #{quote_label(@options['label'])}"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Convert all the options except `label' and options with nil values
|
|
254
|
+
# straight into name = value pairs. Then toss out any resulting nil
|
|
255
|
+
# entries in the final array.
|
|
256
|
+
stringified_options = @options.collect do |name, val|
|
|
257
|
+
unless name == 'label' || val.nil? then
|
|
258
|
+
leader + indent + "#{quote_ID(name)} = #{quote_ID(val)}"
|
|
259
|
+
end
|
|
260
|
+
end.compact
|
|
261
|
+
# Append the specially computed label option.
|
|
262
|
+
stringified_options.push(label_option) unless label_option.nil?
|
|
263
|
+
# Join them all together.
|
|
264
|
+
stringified_options = stringified_options.join(",\n")
|
|
265
|
+
|
|
266
|
+
# Put it all together into a single string with indentation and return the
|
|
267
|
+
# result.
|
|
268
|
+
if stringified_options.empty? then
|
|
269
|
+
return leader + quote_ID(@name) unless @name.nil?
|
|
270
|
+
return nil
|
|
271
|
+
else
|
|
272
|
+
return leader + (@name.nil? ? '' : quote_ID(@name) + " ") + "[\n" +
|
|
273
|
+
stringified_options + "\n" +
|
|
274
|
+
leader + "]"
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
end # class Node
|
|
279
|
+
|
|
280
|
+
# A graph representation. Whether or not it is rendered as directed or
|
|
281
|
+
# undirected depends on which of the programs *dot* or *neato* is used to
|
|
282
|
+
# process and render the graph.
|
|
283
|
+
class Graph < Element
|
|
284
|
+
|
|
285
|
+
# Creates a new Graph with the _params_ Hash providing settings for all
|
|
286
|
+
# graph options. The _option_list_ parameter restricts those options to the
|
|
287
|
+
# list of valid names it contains. The exception to this is the _elements_
|
|
288
|
+
# option which, if specified, must be an Enumerable containing a list of
|
|
289
|
+
# nodes, edges, and/or subgraphs.
|
|
290
|
+
def initialize (params = {}, option_list = GRAPH_OPTS)
|
|
291
|
+
super(params, option_list)
|
|
292
|
+
@elements = params['elements'] ? params['elements'] : []
|
|
293
|
+
@dot_string = 'graph'
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Calls _block_ once for each node, edge, or subgraph contained by this
|
|
297
|
+
# graph, passing the node, edge, or subgraph to the block.
|
|
298
|
+
#
|
|
299
|
+
# :call-seq:
|
|
300
|
+
# graph.each_element {|element| block} -> graph
|
|
301
|
+
#
|
|
302
|
+
def each_element (&block)
|
|
303
|
+
@elements.each(&block)
|
|
304
|
+
self
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Adds a new node, edge, or subgraph to this graph.
|
|
308
|
+
#
|
|
309
|
+
# :call-seq:
|
|
310
|
+
# graph << element -> graph
|
|
311
|
+
#
|
|
312
|
+
def << (element)
|
|
313
|
+
@elements << element
|
|
314
|
+
self
|
|
315
|
+
end
|
|
316
|
+
alias :push :<<
|
|
317
|
+
|
|
318
|
+
# Removes the most recently added node, edge, or subgraph from this graph
|
|
319
|
+
# and returns it.
|
|
320
|
+
#
|
|
321
|
+
# :call-seq:
|
|
322
|
+
# graph.pop -> element
|
|
323
|
+
#
|
|
324
|
+
def pop
|
|
325
|
+
@elements.pop
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Returns a string representation of this graph which is consumable by the
|
|
329
|
+
# graphviz tools +dot+ and +neato+. The _leader_ parameter is used to indent
|
|
330
|
+
# every line of the returned string, and the _indent_ parameter is used to
|
|
331
|
+
# additionally indent nested items.
|
|
332
|
+
def to_s (leader = '', indent = ' ')
|
|
333
|
+
hdr = leader + @dot_string + (@name.nil? ? '' : ' ' + quote_ID(@name)) + " {\n"
|
|
334
|
+
|
|
335
|
+
options = @options.to_a.collect do |name, val|
|
|
336
|
+
unless val.nil? then
|
|
337
|
+
if name == 'label' then
|
|
338
|
+
leader + indent + "#{quote_ID(name)} = #{quote_label(val)}"
|
|
339
|
+
else
|
|
340
|
+
leader + indent + "#{quote_ID(name)} = #{quote_ID(val)}"
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end.compact.join( "\n" )
|
|
344
|
+
|
|
345
|
+
elements = @elements.collect do |element|
|
|
346
|
+
element.to_s(leader + indent, indent)
|
|
347
|
+
end.join("\n\n")
|
|
348
|
+
hdr + (options.empty? ? '' : options + "\n\n") +
|
|
349
|
+
(elements.empty? ? '' : elements + "\n") + leader + "}"
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
end # class Graph
|
|
353
|
+
|
|
354
|
+
# A digraph is a directed graph representation which is the same as a Graph
|
|
355
|
+
# except that its header in dot notation has an identifier of _digraph_
|
|
356
|
+
# instead of _graph_.
|
|
357
|
+
class Digraph < Graph
|
|
358
|
+
|
|
359
|
+
# Creates a new Digraph with the _params_ Hash providing settings for all
|
|
360
|
+
# graph options. The _option_list_ parameter restricts those options to the
|
|
361
|
+
# list of valid names it contains. The exception to this is the _elements_
|
|
362
|
+
# option which, if specified, must be an Enumerable containing a list of
|
|
363
|
+
# nodes, edges, and/or subgraphs.
|
|
364
|
+
def initialize (params = {}, option_list = GRAPH_OPTS)
|
|
365
|
+
super(params, option_list)
|
|
366
|
+
@dot_string = 'digraph'
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
end # class Digraph
|
|
370
|
+
|
|
371
|
+
# A subgraph is a nested graph element and is the same as a Graph except
|
|
372
|
+
# that its header in dot notation has an identifier of _subgraph_ instead of
|
|
373
|
+
# _graph_.
|
|
374
|
+
class Subgraph < Graph
|
|
375
|
+
|
|
376
|
+
# Creates a new Subgraph with the _params_ Hash providing settings for
|
|
377
|
+
# all graph options. The _option_list_ parameter restricts those options to
|
|
378
|
+
# list of valid names it contains. The exception to this is the _elements_
|
|
379
|
+
# option which, if specified, must be an Enumerable containing a list of
|
|
380
|
+
# nodes, edges, and/or subgraphs.
|
|
381
|
+
def initialize (params = {}, option_list = GRAPH_OPTS)
|
|
382
|
+
super(params, option_list)
|
|
383
|
+
@dot_string = 'subgraph'
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
end # class Subgraph
|
|
387
|
+
|
|
388
|
+
# This is an undirected edge representation.
|
|
389
|
+
class Edge < Element
|
|
390
|
+
|
|
391
|
+
# A node or subgraph reference or instance to be used as the starting point
|
|
392
|
+
# for an edge.
|
|
393
|
+
attr_accessor :from
|
|
394
|
+
# A node or subgraph reference or instance to be used as the ending point
|
|
395
|
+
# for an edge.
|
|
396
|
+
attr_accessor :to
|
|
397
|
+
|
|
398
|
+
# Creates a new Edge with the _params_ Hash providing settings for all
|
|
399
|
+
# edge options. The _option_list_ parameter restricts those options to the
|
|
400
|
+
# list of valid names it contains.
|
|
401
|
+
def initialize (params = {}, option_list = EDGE_OPTS)
|
|
402
|
+
super(params, option_list)
|
|
403
|
+
@from = params['from'] ? params['from'] : nil
|
|
404
|
+
@to = params['to'] ? params['to'] : nil
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# Returns a string representation of this edge which is consumable by the
|
|
408
|
+
# graphviz tools +dot+ and +neato+. The _leader_ parameter is used to indent
|
|
409
|
+
# every line of the returned string, and the _indent_ parameter is used to
|
|
410
|
+
# additionally indent nested items.
|
|
411
|
+
def to_s (leader = '', indent = ' ')
|
|
412
|
+
stringified_options = @options.collect do |name, val|
|
|
413
|
+
unless val.nil? then
|
|
414
|
+
leader + indent + "#{quote_ID(name)} = #{quote_ID(val)}"
|
|
415
|
+
end
|
|
416
|
+
end.compact.join( ",\n" )
|
|
417
|
+
|
|
418
|
+
f_s = @from || ''
|
|
419
|
+
t_s = @to || ''
|
|
420
|
+
if stringified_options.empty? then
|
|
421
|
+
leader + quote_ID(f_s) + ' ' + edge_link + ' ' + quote_ID(t_s)
|
|
422
|
+
else
|
|
423
|
+
leader + quote_ID(f_s) + ' ' + edge_link + ' ' + quote_ID(t_s) + " [\n" +
|
|
424
|
+
stringified_options + "\n" +
|
|
425
|
+
leader + "]"
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
private
|
|
430
|
+
def edge_link
|
|
431
|
+
'--'
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
end # class Edge
|
|
435
|
+
|
|
436
|
+
# A directed edge representation otherwise identical to Edge.
|
|
437
|
+
class DirectedEdge < Edge
|
|
438
|
+
|
|
439
|
+
private
|
|
440
|
+
def edge_link
|
|
441
|
+
'->'
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
end # class DirectedEdge
|
|
445
|
+
end; end # module RGL; module DOT
|