laser 0.7.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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,298 @@
|
|
1
|
+
# Type Annotation Grumblings
|
2
|
+
|
3
|
+
I'm trying to hammer out how I want type annotations for Ruby methods
|
4
|
+
and/or classes to work. I usually only think about methods because, well,
|
5
|
+
they're damn-near everything in Ruby.
|
6
|
+
|
7
|
+
First off, this is just for types. All other annotations will just be
|
8
|
+
using YARD's parser, straight up. I'm just gonna fork it.
|
9
|
+
|
10
|
+
I know I want to support annotations that "say" something about the type,
|
11
|
+
because my idea is that Rubyists use Ruby for its expressivity. They expect
|
12
|
+
every line to mean something, damn it. So I just don't accept that [DRuby](http://www.cs.umd.edu/projects/PL/druby/)
|
13
|
+
has this right – mainly because I can't convince *myself* to use it.
|
14
|
+
|
15
|
+
The type system has gotta have a simple – and I mean literally, closest to
|
16
|
+
the fewest characters possible – way to describe each case.
|
17
|
+
|
18
|
+
Given an expression `E`, I want the user to be able to simply tell Laser:
|
19
|
+
|
20
|
+
* `E` has class `T`.
|
21
|
+
* `E` is a subtype of `E`.
|
22
|
+
* `E` is a subclass of `E`. (Not the same as subtype. We know this by now!)
|
23
|
+
* `E` is a supertype of `E`.
|
24
|
+
* `E` is a superclass of `E`.
|
25
|
+
* `E`'s class, or one of `E`'s superclasses, includes module `M`. (Could easily piggyback off of the subclass mechanism)
|
26
|
+
* `E`'s class, or one of `E`'s superclasses, extends module `M`.
|
27
|
+
* `E`'s class is of class (using `C\+\+`/everyone else notation) `S<T>`: a type
|
28
|
+
`S` parameterized on type `T` (or any tuple of types).
|
29
|
+
|
30
|
+
Those are the easy ones. Now for something better/weirder:
|
31
|
+
|
32
|
+
## Better Syntax for Parameterized types.
|
33
|
+
|
34
|
+
First off: which would you prefer?
|
35
|
+
|
36
|
+
# opts: CustomOptionsClass<Symbol, String>
|
37
|
+
def compute(opts=DEFAULT_OPTIONS)
|
38
|
+
end
|
39
|
+
|
40
|
+
or:
|
41
|
+
|
42
|
+
# opts: CustomOptionsClass<Symbol => String>
|
43
|
+
def compute(opts=DEFAULT_OPTIONS)
|
44
|
+
end
|
45
|
+
|
46
|
+
I prefer the latter. Oh, and as a synonym for `Hash<Symbol => String>`:
|
47
|
+
|
48
|
+
# opts: Symbol => String
|
49
|
+
def compute(opts=DEFAULT_OPTIONS)
|
50
|
+
end
|
51
|
+
|
52
|
+
Now, a function type is just a mapping from a tuple of types to a return type. So I'd like to allow both Go syntax:
|
53
|
+
|
54
|
+
# compute_proc: (String, MyLib::TreeNode) Integer
|
55
|
+
|
56
|
+
and a normal arrow syntax:
|
57
|
+
|
58
|
+
# compute_proc: (String, MyLib::TreeNode) -> Fixnum= | Bignum=
|
59
|
+
|
60
|
+
It's natural for people to group arguments together with parens and separate them with commas. They will with Laser's syntax.
|
61
|
+
|
62
|
+
## Union Types
|
63
|
+
|
64
|
+
Crucial. Vertical bar. String | Symbol.
|
65
|
+
|
66
|
+
## Imports? Kinda!
|
67
|
+
|
68
|
+
`E`'s class is defined in module M or one of M's submodules (or sub-submodules... the whole set of modules in M's module tree). This could be *extremely* useful to a gem author to ensure that his library's boundaries are well-defined: if I make it easy enough, a Ruby user could say "Everything in module Laser uses only top-level classes and classes defined in Laser." With a smart enough annotation system, I could make it so a user writes something like this (throwaway syntax), where "in Amp" means "was defined in ::Amp or a submodule" and "in ::!" means "defined in the top-level, and NOT a submodule." "in ::!" would likely be added by default, actually – who could write Ruby without the top level? So yeah – I'm guessing have the default argument type of course be "completely unknown," but as soon as you activate a "defined in M" annotation somewhere, it implicitly includes "in the top-level and only top-level" as an other possibility (hence the Union in the throwaway syntax).
|
69
|
+
|
70
|
+
# Laser: defaults.argument_type = in Amp | in ::!
|
71
|
+
|
72
|
+
## Strict Unknowns
|
73
|
+
|
74
|
+
A lot of times you might not know a type (by itself or as a type parameter) but want to have it checked to be add the requirement that that type be *consistent with some other constraint across global usage*. The obvious constraint would be type compatibility or class compatibility, though the 'defined in module M' is another common one you might consider. Simple use case: marking that you return object `X` which was returned to you by a library. Who knows what class it is? You don't care, but as long as you don't accidentally return a `String` there instead of a `SomeLibrary::UselessModule::Ruby19Hack::XMLNode` you never knew existed. This has *got* to be simple to express for a Ruby type system. I'm considering `?` but worry it will be confused with the Java covariance/contravariance `? super Y`/`? extends Z` notations. Maybe '?!', with '?' meaning 'no idea,' and '?!' meaning 'no idea, but keep it strict.' I like the idea of using '!' as a way to easily add strictness to certain rules, since I hope for the defaults for most annotations to be "the most common" scenarios. Contravariance, for example, is typical in generic type parameters for argument parameters. I intend to ask the developer to add extra characters to their annotation to request invariance or covariance in that position. The same holds true for covariance in return values, invariance in container type parameters (which I expect to be more common but still not as common as covariance).
|
75
|
+
|
76
|
+
## Duh: Duck Typing
|
77
|
+
|
78
|
+
Responds-to. Has to be dead simple and *natural*. I like the idea of using hashes:
|
79
|
+
|
80
|
+
# @return-type #read
|
81
|
+
def get_stream
|
82
|
+
MagicIOFairy.visit
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return-type #write(#to_s)
|
86
|
+
def get\_universal\_writer
|
87
|
+
stream = MagicIOFairy.visit
|
88
|
+
def stream.write(y)
|
89
|
+
super(y.to_s)
|
90
|
+
end
|
91
|
+
stream
|
92
|
+
end
|
93
|
+
|
94
|
+
but I'm not sure yet.
|
95
|
+
|
96
|
+
## Motherfuckin' Tuples.
|
97
|
+
|
98
|
+
The *most common API is Ruby* is Rack's #call syntax, and it must return a tuple!
|
99
|
+
|
100
|
+
DRuby has it so I better have it. I like their syntax:
|
101
|
+
|
102
|
+
# @return-type (Fixnum, String => String, String)
|
103
|
+
def call
|
104
|
+
[200, {'content-type' => 'text/plain'}, 'Hello, World!']
|
105
|
+
end
|
106
|
+
|
107
|
+
For some reason, for them Tuples are subtypes of "Top". That's.... foolish.
|
108
|
+
`Tuple<T1, T2, ..., Tn>` is clearly a subtype of `Array<BasicObject>` if not
|
109
|
+
`Array<SomeMoreSpecificCommonSuperclass>`. It's actually a dependent type, which DRuby
|
110
|
+
doesn't do in a general way. I intend to address dependent types in at least a limited
|
111
|
+
fashion. I will definitely allow for dynamic semantic checking of dependent types, but
|
112
|
+
the really interesting part is of course the static analysis. I'm not writing a proof
|
113
|
+
checker like Coq's, thank you very much.
|
114
|
+
|
115
|
+
Not much else to say. I think it may introduce ambiguity with my ideas for function
|
116
|
+
type annotations, though. I was going to have a function that had no interesting return
|
117
|
+
type be able to be left out. However that means that an n-argument function type with no
|
118
|
+
interesting return type is *the same type* as a function from that 1 argument to... nothing,
|
119
|
+
since it's now of type `(ArgType) -> NothingInteresting`. If you drop the NothingInteresting,
|
120
|
+
your syntax is `(ArgType) -> ` or `(ArgType)`. To me, the first is clearly a function to void,
|
121
|
+
but it means the programmer has to type out something for something that doesn't matter. The
|
122
|
+
latter is ambiguous with the 1-argument tuple type `(ArgType)`. Can't have ambiguity. Not for
|
123
|
+
a mini annotation language i want people to use foreals. However,
|
124
|
+
there is one very good thing: no object in Ruby itself is "callable" - you can't do:
|
125
|
+
|
126
|
+
x = FooBar.new
|
127
|
+
x(5)
|
128
|
+
|
129
|
+
and expect that to call a function. That's because it will look to call a method. So we actually
|
130
|
+
don't have any objects with names that are of function type. So whenever you annotate something callable,
|
131
|
+
you're using the #call idiom, and so 'function types' get swept away to structural typing! I think
|
132
|
+
I may provide a special syntax as a shortcut for #call, as it's a common thing to structurally type
|
133
|
+
(even `Rack` picked it – it's a great name for a `Runnable` (from Java) or Functor method).
|
134
|
+
|
135
|
+
# list: Array<String>
|
136
|
+
# proc: (String)
|
137
|
+
def each_proc(list, proc)
|
138
|
+
list.each {|x| proc.call(x)}
|
139
|
+
end
|
140
|
+
|
141
|
+
The one time you might want to annotate a "function type" is when you're describing a method in
|
142
|
+
what I call *compact* form: it's just a type declaration all at once, no separate lines for
|
143
|
+
each argument or return type. I refuse to require you to type the name of a method in the docs
|
144
|
+
just above the method, so it's just a function type:
|
145
|
+
|
146
|
+
# (Fixnum, Fixnum) -> Fixnum= | Bignum=
|
147
|
+
def add(a, b)
|
148
|
+
a + b
|
149
|
+
end
|
150
|
+
|
151
|
+
A method with 1 argument and an uninteresting return type is thus annotated as such:
|
152
|
+
|
153
|
+
# (#to_s) ->
|
154
|
+
def write(x)
|
155
|
+
@stream.write(x)
|
156
|
+
end
|
157
|
+
|
158
|
+
or
|
159
|
+
|
160
|
+
# (#to_s)
|
161
|
+
def write(x)
|
162
|
+
@stream.write(x)
|
163
|
+
end
|
164
|
+
|
165
|
+
Which is the same as a 1-argument tuple. However: I *know* in this case that the syntax is being used
|
166
|
+
to describe a function, never a 1-argument tuple. So there is no ambiguity here. All other objects of
|
167
|
+
function type would need to use the name of a method, so I'm in the clear for ambiguity. However, I
|
168
|
+
will remind users that this is there but probably won't bite them.
|
169
|
+
|
170
|
+
## Customizable annotation syntax?
|
171
|
+
|
172
|
+
Switch between Haskell-like and C-like? Specify a function as `Fixnum doubler(Fixnum)` instead of go-like syntax?
|
173
|
+
|
174
|
+
## Method Generation
|
175
|
+
|
176
|
+
Most importantly added to method_missing as an annotation, but could possibly be moved to other places: listing generated methods. Now, you might want to just list a bunch of methods that will get created at load time:
|
177
|
+
|
178
|
+
# generates: doubler(#to_i) Fixnum
|
179
|
+
# generates: tripler(#to_i) Fixnum
|
180
|
+
# generates: quadrupler(#to_i) Float
|
181
|
+
[[2, 'doubler'],[3, 'tripler'],[4.0,'quadupler']].each do |num, name|
|
182
|
+
define_method name do |y|
|
183
|
+
y.to_i * num
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
Which would generate type notes for 3 methods it then assumes exist with those annotations. Of course you can specify multiple overloads for these just by adding more lines:
|
188
|
+
|
189
|
+
# generates: doubler(Fixnum) Fixnum
|
190
|
+
# overflow lawlz
|
191
|
+
# generates: doubler(Fixnum) Bignum
|
192
|
+
|
193
|
+
## Varargs
|
194
|
+
|
195
|
+
DRuby screwed the pooch on this one. First of all, you can tell just by looking at the damn method declaration that the argument is a vararg list. It already has a damn star on it. Also, they talk in their manual about how it makes "parameter list types as first-class types". I'm not entirely clear about that, because a parameter list in Ruby is just an `Array`. Anyway, I think it's important for people to be able to have
|
196
|
+
a way to note in the type declaration that something is a vararg. Or they could do that with `Array<Type>`. Or they might want me to infer it from the big effing star in their argument list.
|
197
|
+
|
198
|
+
I think I'm going to allow `*` to denote a vararg, and require its use if you want to enter a type annotation for something. No just writing "String" and letting me figure out that of course it's `Array<String>`. (Unless, of course, you use a certain flag, since I'll be happy to let you shoot yourself in the foot for it. Oh, and it won't work if it's an Array of Arrays, because there'd be ambiguity in the grammar there.)
|
199
|
+
|
200
|
+
## Arbitrary annotations
|
201
|
+
|
202
|
+
Things like "could_raise?" (for functions) or "pure" (for functions) are going to be accessible from the type system. Since those are just going to be instances of a more general labeling system the user can extend themselves, that means the user can define their own tags. For example, a user wants to eventually deprecate an API. They want to do it quick and dirty, so they implement the "[v1]" tag. Take your V1API module, slap the entire module with [v1]. Bam. Every class in that module is now deprecated. Then later, when you think you're done with the migration, require [v1=false] on your arguments to a method. If it fails type-checking, then that means I can prove a non-deprecated object will make its way to to that method, and you must not have fully removed all uses of the deprecated code. A smarter way would be of course to deprecate 1 method at a time, by adding the @deprecated or [v1] tag around. I could integrate this with YARD's existing tag system, though I don't want to get mucked up in Documentation-related code.
|
203
|
+
|
204
|
+
## Overloads
|
205
|
+
|
206
|
+
Overloads have to be done right, because they're pretty common. Especially in the standard library, and I'm going to have to type these damn annotations.
|
207
|
+
|
208
|
+
For one, you could just write each new possibility as separate lines, as DRuby does.
|
209
|
+
|
210
|
+
# (Fixnum) -> Fixnum | nil
|
211
|
+
# (Fixnum, Fixnum) -> String | nil
|
212
|
+
def slice(*args)
|
213
|
+
end
|
214
|
+
|
215
|
+
Of course slice has all kinds of other overloads, but this is simple enough. Notice that | binds tighter than the method arrow. That means it also works just fine for the "elided arrow" style that Go uses:
|
216
|
+
|
217
|
+
# (Fixnum, Fixnum) String | nil
|
218
|
+
# |---------------||-----|-|---|
|
219
|
+
# |---------------||-----------|
|
220
|
+
# |----------------------------|
|
221
|
+
|
222
|
+
Yay for a bootleg ascii concrete parse tree.
|
223
|
+
|
224
|
+
It's fine with me, to be honest.
|
225
|
+
|
226
|
+
## Blocks?
|
227
|
+
|
228
|
+
How to annotate blocks? Not sure yet. Explicit blocks as arguments need to be annotated as well, though
|
229
|
+
they're another case of simple structural typing on #call. Oh, and I want to be able to annotate
|
230
|
+
how many *times* the block gets called. Possibly as a function of the arguments, so this could be
|
231
|
+
profiled at runtime. See contracts later.
|
232
|
+
|
233
|
+
But yeah, specifying that either the current function yields and what it yields is important. What
|
234
|
+
the block itself returns is also important. I saw DRuby trying to specify the type for Proc#initialize
|
235
|
+
and it made my brain hurt, even though Proc#initialize is just a no-arg function, that takes a block
|
236
|
+
with args and a return value, and returns #call(ArgTuple -> RetType). But the declaration was horrid:
|
237
|
+
|
238
|
+
##% initialize: () {(^args) -> ret} -> Proc<^args, ret>
|
239
|
+
def initialize(); end
|
240
|
+
|
241
|
+
You couldn't pay me to type that out. Do I know better? Not sure yet, but for one, I'd prefer this:
|
242
|
+
|
243
|
+
# {*args -> ret} -> #call(*args) -> ret
|
244
|
+
def initialize; end
|
245
|
+
|
246
|
+
But even that feels like pushing it to me.
|
247
|
+
|
248
|
+
# {*args ret} #call(*args) ret
|
249
|
+
|
250
|
+
Is what that example would be with the unnecessary arrows. I like that the braces so "this stuff is
|
251
|
+
block stuff!" The more symbols I use, the more arcane it becomes, unfortunately. I bet people would
|
252
|
+
prefer more english words, but I should look into that first. DRuby uses `or` for Union types, after all.
|
253
|
+
|
254
|
+
## Contracts
|
255
|
+
|
256
|
+
Used as a preprocessor, Laser could inject contracts into specified locations in methods. These could
|
257
|
+
be used for both assertions (invariants) and to profile code. So the user should be able to have simple
|
258
|
+
syntax for common contracts, but also have the fully power of arbitrary Ruby to put in there. That way,
|
259
|
+
you run in dev mode with the contracts on, and when run in production *all those assertions disappear*.
|
260
|
+
But the nice thing is, they're still there *as documentation*!
|
261
|
+
|
262
|
+
I haven't fleshed this out enough and it's going to be the last part of my work. Loren Segal is working
|
263
|
+
in this area as well.
|
264
|
+
|
265
|
+
## Dependent Types
|
266
|
+
|
267
|
+
How much dependent typing can I support? Literals lend themselves to a few (array length forming a tuple),
|
268
|
+
but beyond those? How about: the keys of this hash come from this set. That's a good one. Rails probably
|
269
|
+
wastes tons of time manually checking those. Of course, they'd never *remove* them to protect users... but they'd know during
|
270
|
+
development that their own code isn't using them wrong without running the code to find out!
|
271
|
+
Oh, and whether a string is frozen or not, though this could be done with a generic type over TrueClass and
|
272
|
+
FalseClass. Technically, no need to invoke dependent types.
|
273
|
+
|
274
|
+
## Detecting Data Dependencies?
|
275
|
+
|
276
|
+
Here's an interesting note about this: I may not be able to do dependent type checking without a hugely
|
277
|
+
complicated proof checker, but by allowing the annotation of dependent types, I can find *data dependencies*
|
278
|
+
whenever code converts a non-dependent version of the type (ie: Hash<Symbol => Object>) to the dependent one,
|
279
|
+
(i.e. Hash<SymbolInSomeSet => Object>), without an annotation, we are observing the moment when the code makes
|
280
|
+
the assumption that the the object is of the correct dependent type.
|
281
|
+
|
282
|
+
For example, if you read in a configuration YAML file and assign keys to values in a Hash in a method #read_file, you could have this
|
283
|
+
dependent type annotation saying that #read_file returns an `Hash<SymbolInSomeSet => Object>` object, then
|
284
|
+
when the code executes `hash[key.to_sym] = value`, that `key.to_sym` is of type `Symbol`, not `SymbolInSomeSet`.
|
285
|
+
Since `Hash<SomeSymbolInSomeSet => Object>`'s `[]=` method is of type `(SomeSymbolInSomeSet) -> Object`, we
|
286
|
+
must coerce SomeSymbolInSomeSet into a Symbol. This is the data dependency, and our general-purpose static
|
287
|
+
analysis tool can detect them and require a disclaimer saying "hey, i know this is a data dependency here."
|
288
|
+
|
289
|
+
## Mutability
|
290
|
+
|
291
|
+
When annotating a type, you should mark if it will be mutated. That way you don't have to type when something
|
292
|
+
is `const` or immutable. As it should be.
|
293
|
+
|
294
|
+
I like the idea of using `!` to indicate mutation, so perhaps just adding a ! at the end of a type adds [mutable = true] as a custom annotation (described above).
|
295
|
+
|
296
|
+
## Refinements
|
297
|
+
|
298
|
+
It'd be huge to support these out the gate. It should be inferable extremely easily.
|
@@ -0,0 +1,6 @@
|
|
1
|
+
Just noticed that the "Strict Unknowns" I described yesterday is already in-place
|
2
|
+
with only 1 specific constraint (super/sub-typing from a given class). I kind of
|
3
|
+
like my addition of "no idea but ensure consistency across calls" idea, and allowing
|
4
|
+
generic constraints is awesome.
|
5
|
+
|
6
|
+
Also, Scala uses `_`to mean "unknown", so I'm stealing it.
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'laser/third_party/rgl/traversal'
|
4
|
+
require 'laser/third_party/rgl/connected_components'
|
5
|
+
require 'laser/third_party/rgl/adjacency'
|
6
|
+
require_relative 'test_helper'
|
7
|
+
|
8
|
+
include RGL
|
9
|
+
|
10
|
+
def graph_from_string(s)
|
11
|
+
g = DirectedAdjacencyGraph.new(Array)
|
12
|
+
s.split(/\n/).collect{|x| x.split(/->/)}.each do |a|
|
13
|
+
from = a[0].strip
|
14
|
+
a[1].split.each do |to|
|
15
|
+
g.add_edge from,to
|
16
|
+
end
|
17
|
+
end
|
18
|
+
g
|
19
|
+
end
|
20
|
+
|
21
|
+
class TestComponents < Test::Unit::TestCase
|
22
|
+
|
23
|
+
def setup
|
24
|
+
@dg = DirectedAdjacencyGraph.new(Array)
|
25
|
+
edges = [[1,2],[2,3],[2,4],[4,5],[1,6],[6,4]]
|
26
|
+
edges.each do |(src,target)|
|
27
|
+
@dg.add_edge(src, target)
|
28
|
+
end
|
29
|
+
@bfs = @dg.bfs_iterator(1)
|
30
|
+
@dfs = @dg.dfs_iterator(1)
|
31
|
+
|
32
|
+
@ug = AdjacencyGraph.new(Array)
|
33
|
+
@ug.add_edges(*edges)
|
34
|
+
|
35
|
+
@dg2 = graph_from_string(
|
36
|
+
"a -> b f h
|
37
|
+
b -> c a
|
38
|
+
c -> d b
|
39
|
+
d -> e
|
40
|
+
e -> d
|
41
|
+
f -> g
|
42
|
+
g -> f d
|
43
|
+
h -> i
|
44
|
+
i -> h j e c")
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_connected_components
|
48
|
+
ccs = []
|
49
|
+
@ug.each_connected_component { |c| ccs << c }
|
50
|
+
assert_equal(1,ccs.size)
|
51
|
+
|
52
|
+
ccs = []
|
53
|
+
@ug.add_edge 10,11
|
54
|
+
@ug.add_edge 33,44
|
55
|
+
@ug.each_connected_component { |c| ccs << c.sort }
|
56
|
+
assert_equal([[10, 11], [1, 2, 3, 4, 5, 6], [33,44]].sort,ccs.sort)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_strong_components
|
60
|
+
vis = @dg2.strongly_connected_components
|
61
|
+
|
62
|
+
assert_equal(4,vis.num_comp)
|
63
|
+
assert_equal([["a", 3], ["b", 3], ["c", 3], ["d", 0], ["e", 0], ["f", 1], ["g", 1], ["h", 3], ["i", 3], ["j", 2]],vis.comp_map.to_a)
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
$LOAD_PATH << "../lib"
|
2
|
+
require 'test/unit'
|
3
|
+
require 'laser/third_party/rgl/adjacency'
|
4
|
+
require_relative 'test_helper'
|
5
|
+
|
6
|
+
include RGL
|
7
|
+
|
8
|
+
class TestCycles < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@dg = DirectedAdjacencyGraph.new(Array)
|
12
|
+
edges = [[1,2],[2,2],[2,3],[3,4],[4,5],[5,1],[6,4],[6,6],[1,4],[7,7],[7,7]]
|
13
|
+
edges.each do |(src,target)|
|
14
|
+
@dg.add_edge(src, target)
|
15
|
+
end
|
16
|
+
|
17
|
+
@ug = AdjacencyGraph.new(Array)
|
18
|
+
@ug.add_edges(*edges)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Helper for testing for different permutations of a cycle
|
22
|
+
def contains_cycle?(cycles,cycle)
|
23
|
+
cycle.size.times do |i|
|
24
|
+
return true if cycles.include?(cycle)
|
25
|
+
cycle = cycle[1..-1] + [cycle[0]]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_cycles
|
30
|
+
d_cycles = @dg.cycles
|
31
|
+
assert_equal 6, d_cycles.size
|
32
|
+
assert d_cycles.include?([6])
|
33
|
+
assert d_cycles.include?([7])
|
34
|
+
assert d_cycles.include?([2])
|
35
|
+
assert contains_cycle?(d_cycles, [1,4,5])
|
36
|
+
assert contains_cycle?(d_cycles, [1,2,3,4,5])
|
37
|
+
|
38
|
+
assert_equal 5, DirectedAdjacencyGraph.new(Set, @dg).cycles.size
|
39
|
+
|
40
|
+
u_cycles = AdjacencyGraph.new(Set, @dg).cycles.sort
|
41
|
+
|
42
|
+
assert u_cycles.include?([2])
|
43
|
+
assert u_cycles.include?([6])
|
44
|
+
assert u_cycles.include?([7])
|
45
|
+
assert contains_cycle?(u_cycles, [1,2,3,4,5])
|
46
|
+
assert contains_cycle?(u_cycles, [1,5,4,3,2])
|
47
|
+
assert contains_cycle?(u_cycles, [1,4,3,2])
|
48
|
+
assert contains_cycle?(u_cycles, [1,4,5])
|
49
|
+
assert contains_cycle?(u_cycles, [1,5,4])
|
50
|
+
assert contains_cycle?(u_cycles, [1,5])
|
51
|
+
assert contains_cycle?(u_cycles, [1,2])
|
52
|
+
assert contains_cycle?(u_cycles, [1,2,3,4])
|
53
|
+
assert contains_cycle?(u_cycles, [2,3])
|
54
|
+
assert contains_cycle?(u_cycles, [1,4])
|
55
|
+
assert contains_cycle?(u_cycles, [3,4])
|
56
|
+
assert contains_cycle?(u_cycles, [4,5])
|
57
|
+
assert contains_cycle?(u_cycles, [4,6])
|
58
|
+
assert_equal 16, u_cycles.size
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'laser/third_party/rgl/adjacency'
|
3
|
+
|
4
|
+
include RGL
|
5
|
+
include RGL::Edge
|
6
|
+
|
7
|
+
class TestDirectedGraph < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@dg = DirectedAdjacencyGraph.new
|
10
|
+
[[1,2],[2,3],[3,2],[2,4]].each do |(src,target)|
|
11
|
+
@dg.add_edge(src, target)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_empty_graph
|
16
|
+
dg = DirectedAdjacencyGraph.new
|
17
|
+
assert dg.empty?
|
18
|
+
assert dg.directed?
|
19
|
+
assert(!dg.has_edge?(2,1))
|
20
|
+
assert(!dg.has_vertex?(3))
|
21
|
+
# Non existend vertex result in a Name Error because each_key is
|
22
|
+
# called for nil
|
23
|
+
assert_raises(NoVertexError) {dg.out_degree(3)}
|
24
|
+
assert_equal([],dg.vertices)
|
25
|
+
assert_equal(0,dg.size)
|
26
|
+
assert_equal(0,dg.num_vertices)
|
27
|
+
assert_equal(0,dg.num_edges)
|
28
|
+
assert_equal(DirectedEdge,dg.edge_class)
|
29
|
+
assert([].eql?(dg.edges))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_add
|
33
|
+
dg = DirectedAdjacencyGraph.new
|
34
|
+
dg.add_edge(1,2)
|
35
|
+
assert(!dg.empty?)
|
36
|
+
assert(dg.has_edge?(1,2))
|
37
|
+
assert(!dg.has_edge?(2,1))
|
38
|
+
assert(dg.has_vertex?(1) && dg.has_vertex?(2))
|
39
|
+
assert(!dg.has_vertex?(3))
|
40
|
+
|
41
|
+
assert_equal([1,2],dg.vertices.sort)
|
42
|
+
assert([DirectedEdge.new(1,2)].eql?(dg.edges))
|
43
|
+
assert_equal("(1-2)",dg.edges.join)
|
44
|
+
|
45
|
+
assert_equal([2],dg.adjacent_vertices(1))
|
46
|
+
assert_equal([],dg.adjacent_vertices(2))
|
47
|
+
|
48
|
+
assert_equal(1,dg.out_degree(1))
|
49
|
+
assert_equal(0,dg.out_degree(2))
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_edges
|
53
|
+
assert_equal(4, @dg.edges.count)
|
54
|
+
assert_equal([1,2,2,3], @dg.edges.map {|l| l.source}.sort)
|
55
|
+
assert_equal([2,2,3,4], @dg.edges.map {|l| l.target}.sort)
|
56
|
+
assert_equal("(1-2)(2-3)(2-4)(3-2)", @dg.edges.map {|l| l.to_s}.sort.join)
|
57
|
+
# assert_equal([0,1,2,3], @dg.edges.map {|l| l.info}.sort)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_vertices
|
61
|
+
assert_equal([1,2,3,4], @dg.vertices.sort)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_edges_from_to?
|
65
|
+
assert @dg.has_edge?(1,2)
|
66
|
+
assert @dg.has_edge?(2,3)
|
67
|
+
assert @dg.has_edge?(3,2)
|
68
|
+
assert @dg.has_edge?(2,4)
|
69
|
+
assert !@dg.has_edge?(2,1)
|
70
|
+
assert !@dg.has_edge?(3,1)
|
71
|
+
assert !@dg.has_edge?(4,1)
|
72
|
+
assert !@dg.has_edge?(4,2)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_remove_edges
|
76
|
+
@dg.remove_edge 1,2
|
77
|
+
assert !@dg.has_edge?(1,2)
|
78
|
+
@dg.remove_edge 1,2
|
79
|
+
assert !@dg.has_edge?(1,2)
|
80
|
+
@dg.remove_vertex 3
|
81
|
+
assert !@dg.has_vertex?(3)
|
82
|
+
assert !@dg.has_edge?(2,3)
|
83
|
+
assert_equal('(2-4)',@dg.edges.join)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_add_vertices
|
87
|
+
dg = DirectedAdjacencyGraph.new
|
88
|
+
dg.add_vertices 1,3,2,4
|
89
|
+
assert_equal dg.vertices.sort, [1,2,3,4]
|
90
|
+
|
91
|
+
dg.remove_vertices 1,3
|
92
|
+
assert_equal dg.vertices.sort, [2,4]
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_creating_from_array
|
96
|
+
dg = DirectedAdjacencyGraph[1, 2, 3, 4]
|
97
|
+
assert_equal([1,2,3,4], dg.vertices.sort)
|
98
|
+
assert_equal('(1-2)(3-4)', dg.edges.join)
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_reverse
|
102
|
+
reverted = @dg.reverse
|
103
|
+
@dg.each_edge do |u,v|
|
104
|
+
assert(reverted.has_edge?(v,u))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_reverse
|
109
|
+
# Add isolated vertex
|
110
|
+
@dg.add_vertex(42)
|
111
|
+
reverted = @dg.reverse
|
112
|
+
|
113
|
+
@dg.each_edge do |u,v|
|
114
|
+
assert(reverted.has_edge?(v,u))
|
115
|
+
end
|
116
|
+
|
117
|
+
assert(reverted.has_vertex?(42),
|
118
|
+
'Reverted graph should contain isolated Vertex 42')
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_to_undirected
|
122
|
+
undirected = @dg.to_undirected
|
123
|
+
assert_equal '(1=2)(2=3)(2=4)', undirected.edges.sort.join
|
124
|
+
end
|
125
|
+
end
|