xumlidot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/bin/xumlidot +39 -0
  3. data/lib/xumlidot.rb +10 -0
  4. data/lib/xumlidot/diagram.rb +18 -0
  5. data/lib/xumlidot/diagram/dot.rb +55 -0
  6. data/lib/xumlidot/diagram/dot/klass.rb +75 -0
  7. data/lib/xumlidot/diagram/dot/module.rb +11 -0
  8. data/lib/xumlidot/diagram/shared/naming.rb +19 -0
  9. data/lib/xumlidot/diagram/xmi.rb +190 -0
  10. data/lib/xumlidot/diagram/xmi/argument.rb +34 -0
  11. data/lib/xumlidot/diagram/xmi/attribute.rb +24 -0
  12. data/lib/xumlidot/diagram/xmi/constant.rb +16 -0
  13. data/lib/xumlidot/diagram/xmi/id.rb +39 -0
  14. data/lib/xumlidot/diagram/xmi/klass.rb +133 -0
  15. data/lib/xumlidot/diagram/xmi/method.rb +38 -0
  16. data/lib/xumlidot/diagram/xmi/superklass.rb +18 -0
  17. data/lib/xumlidot/directory_tree.rb +24 -0
  18. data/lib/xumlidot/options.rb +104 -0
  19. data/lib/xumlidot/parsers.rb +14 -0
  20. data/lib/xumlidot/parsers/args.rb +159 -0
  21. data/lib/xumlidot/parsers/call.rb +92 -0
  22. data/lib/xumlidot/parsers/file.rb +15 -0
  23. data/lib/xumlidot/parsers/generic.rb +106 -0
  24. data/lib/xumlidot/parsers/klass_definition.rb +76 -0
  25. data/lib/xumlidot/parsers/method_signature.rb +95 -0
  26. data/lib/xumlidot/parsers/module_definition.rb +55 -0
  27. data/lib/xumlidot/parsers/scope.rb +54 -0
  28. data/lib/xumlidot/parsers/stack.rb +96 -0
  29. data/lib/xumlidot/types.rb +18 -0
  30. data/lib/xumlidot/types/argument.rb +61 -0
  31. data/lib/xumlidot/types/arguments.rb +14 -0
  32. data/lib/xumlidot/types/attribute.rb +34 -0
  33. data/lib/xumlidot/types/attributes.rb +11 -0
  34. data/lib/xumlidot/types/constant.rb +49 -0
  35. data/lib/xumlidot/types/constants.rb +57 -0
  36. data/lib/xumlidot/types/inherited_module.rb +24 -0
  37. data/lib/xumlidot/types/instance_methods.rb +10 -0
  38. data/lib/xumlidot/types/klass.rb +60 -0
  39. data/lib/xumlidot/types/klass_definition.rb +61 -0
  40. data/lib/xumlidot/types/klass_methods.rb +10 -0
  41. data/lib/xumlidot/types/method.rb +10 -0
  42. data/lib/xumlidot/types/method_signature.rb +64 -0
  43. data/lib/xumlidot/types/methods.rb +10 -0
  44. data/lib/xumlidot/types/module.rb +11 -0
  45. data/lib/xumlidot/types/module_definition.rb +14 -0
  46. data/lib/xumlidot/types/superklass.rb +32 -0
  47. data/spec/spec_helper.rb +2 -0
  48. metadata +175 -0
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../parsers'
4
+ require_relative '../types'
5
+
6
+ module Xumlidot
7
+ module Parsers
8
+ class Call < MethodBasedSexpProcessor
9
+ attr_reader :definition
10
+
11
+ def initialize(exp, klass)
12
+ super()
13
+
14
+ @klass = klass
15
+ @modules = ::Xumlidot::Types::InheritedModule.new(nil)
16
+
17
+ process(exp)
18
+
19
+ return if klass.definition.nil?
20
+ klass.definition.inherited_modules << @modules
21
+ end
22
+
23
+ def process_call(exp)
24
+ exp.shift # remove the :call
25
+
26
+ begin
27
+ recv = process(exp.shift)
28
+ rescue => e
29
+ STDERR.puts " ** bug: unable to calculate reciever for #{exp}"
30
+ end
31
+
32
+ name = exp.shift
33
+ args = exp.shift
34
+
35
+ case name
36
+ when :private, :public, :protected
37
+ ::Xumlidot::Parsers::Scope.set_visibility(name)
38
+ when :include
39
+ @modules.type = :include
40
+ process(args)
41
+ when :extend
42
+ @modules.type = :extend
43
+ process(args)
44
+ when :prepend
45
+ @modules.type = :prepend
46
+ process(args)
47
+ when :module_function
48
+ # TODO: expose as an instance method on the module
49
+ when :attr_reader
50
+ add_attributes(args, exp, read: true)
51
+ when :attr_writer
52
+ add_attributes(args, exp, write: true)
53
+ when :attr_accessor
54
+ add_attributes(args, exp, read: true, write: true)
55
+ # else
56
+ # puts "CALL RECV:#{recv unless recv.nil? || recv.empty?} NAME:#{name} ARGS:#{args unless args.nil? || args.empty?}"
57
+ end
58
+ s()
59
+ end
60
+
61
+ def add_attributes(name, exp, read: false, write: false)
62
+ @klass.attributes << ::Xumlidot::Types::Attribute.new(name.value, read, write)
63
+ exp.each do |attribute|
64
+ @klass.attributes << ::Xumlidot::Types::Attribute.new(attribute.value, read, write)
65
+ end
66
+ end
67
+
68
+ def process_const(exp)
69
+ # exp.shift # remove :const
70
+ @modules << exp.value
71
+ process_until_empty(exp)
72
+ s()
73
+ end
74
+
75
+ def process_colon2(exp)
76
+ exp.shift # remove :colon2
77
+ @modules << exp.value
78
+ process_until_empty(exp)
79
+ s()
80
+ end
81
+
82
+ def process_colon3(exp)
83
+ exp.shift # remove :colon3
84
+ @modules << '::'
85
+ @modules << exp.value
86
+ process_until_empty(exp)
87
+ s()
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../parsers'
4
+ require_relative '../types'
5
+
6
+ module Xumlidot
7
+ module Parsers
8
+ # Takes a file contents (as a string) and parses it into
9
+ # s expressions using ruby parser. The parse method uses
10
+ # the SexpProcessors methods to contert to a graph
11
+ class File < Generic
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../parsers'
4
+ require_relative '../types'
5
+
6
+ module Xumlidot
7
+ module Parsers
8
+
9
+ # We need a level of indirection between the actual parser
10
+ # and MethodBasedSexpProcessor since we will probably end up inheriting
11
+ # from SexpProcessor directly eventually
12
+ #
13
+ # The File processor was getting too busy and its obvious we want to share
14
+ # some bits of the processing
15
+ class Generic < MethodBasedSexpProcessor
16
+ attr_reader :constants
17
+
18
+ def initialize(string, constants = Stack::Constants.new )
19
+ @parsed = RubyParser.new.parse(string)
20
+ @constants = constants
21
+ super()
22
+ end
23
+
24
+ def parse
25
+ process(@parsed)
26
+ end
27
+
28
+ # CLASSES, MODULES AND DEFINITIONS
29
+ #
30
+ # We process the superclass differently since we dont want an
31
+ # actual superclass node adding - just the methods
32
+ def process_sclass(exp)
33
+ super(exp) do
34
+ Scope.public { process_until_empty(exp) } # Process the superclass with public visibility
35
+ end
36
+ rescue Exception => e
37
+ if ENV["XUMLIDOT_DEBUG"]
38
+ STDERR.puts e.backtrace.reverse
39
+ STDERR.puts "ERROR (#process_sclass) #{e.message}"
40
+ end
41
+ exp
42
+ end
43
+
44
+ def process_module(exp)
45
+ process_class(exp,
46
+ definition_parser: ::Xumlidot::Parsers::ModuleDefinition,
47
+ type: Xumlidot::Types::Module)
48
+ end
49
+
50
+ def process_class(exp,
51
+ definition_parser: ::Xumlidot::Parsers::KlassDefinition,
52
+ type: Xumlidot::Types::Klass)
53
+
54
+ Scope.set_visibility
55
+ definition = definition_parser.new(exp.dup[0..2], @class_stack).definition
56
+
57
+ STDERR.puts definition.to_s if ::Xumlidot::Options.debug == true
58
+ super(exp) do
59
+ Scope.public do
60
+ klass_or_module = type.new(definition)
61
+ @constants.add(klass_or_module)
62
+ process_until_empty(exp)
63
+ end
64
+ end
65
+ rescue Exception => e
66
+ if ENV["XUMLIDOT_DEBUG"]
67
+ STDERR.puts e.backtrace.reverse
68
+ STDERR.puts "ERROR (#process_class) #{e.message}"
69
+ end
70
+ exp
71
+ end
72
+
73
+ # METHODS & METHOD SIGNATURES
74
+ def process_defn(exp, superclass_method = false)
75
+ method = ::Xumlidot::Parsers::MethodSignature.new(exp, superclass_method || @sclass.last)
76
+ @constants.last_added.add_method(method)
77
+ STDERR.puts method.to_s if ::Xumlidot::Options.debug == true
78
+ #super(exp) { process_until_empty(exp) } # DISABLING since parsing the method is crashing
79
+ s()
80
+ rescue Exception => e
81
+ if ENV["XUMLIDOT_DEBUG"]
82
+ STDERR.puts e.backtrace.reverse
83
+ STDERR.puts "ERROR (#process_def#{superclass_method ? 's' : 'n'}) #{e.message}"
84
+ end
85
+ s()
86
+ end
87
+
88
+ def process_defs(exp)
89
+ process_defn(exp, true)
90
+ end
91
+
92
+ # CALLS
93
+ def process_call(exp)
94
+ ::Xumlidot::Parsers::Call.new(exp, @constants.last_added)
95
+ s()
96
+ rescue Exception => e
97
+ if ENV["XUMLIDOT_DEBUG"]
98
+ STDERR.puts e.backtrace.reverse
99
+ STDERR.puts "ERROR (#process_call) #{e.message}"
100
+ end
101
+ s()
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../parsers'
4
+ require_relative '../types'
5
+
6
+ module Xumlidot
7
+ module Parsers
8
+ # Parser for the KLASS DEFINITION ONLY
9
+ class KlassDefinition < MethodBasedSexpProcessor
10
+
11
+ attr_reader :definition
12
+
13
+ def initialize(exp, namespace = nil)
14
+ super()
15
+
16
+ @definition = ::Xumlidot::Types::KlassDefinition.new
17
+ @namespace = namespace.dup
18
+
19
+ process(exp)
20
+ end
21
+
22
+ def process_class(exp)
23
+ exp.shift # remove :class
24
+ definition = exp.shift
25
+
26
+ # Processes the name of the class
27
+ if Sexp === definition
28
+ case definition.sexp_type
29
+ when :colon2 then # Reached in the event that a name is a compound
30
+ name = definition.flatten
31
+ name.delete :const
32
+ name.delete :colon2
33
+ name.each do |v|
34
+ @definition.name << ::Xumlidot::Types::Constant.new(v, @namespace)
35
+ end
36
+ when :colon3 then # Reached in the event that a name begins with ::
37
+ @definition.name << ::Xumlidot::Types::Constant.new(definition.last, '::')
38
+ else
39
+ raise "unknown type #{exp.inspect}"
40
+ end
41
+ else Symbol === definition
42
+ #if we have a symbol we have the actual class name
43
+ # e.g. class Foo; end
44
+ @definition.name << ::Xumlidot::Types::Constant.new(definition, @namespace)
45
+ end
46
+
47
+ # Processess inheritance
48
+ process_until_empty(exp)
49
+
50
+ s()
51
+ end
52
+
53
+ def process_const(exp)
54
+ # TODO: may have removed a shift by mistake
55
+ @definition.superklass << exp.value
56
+ process_until_empty(exp)
57
+ s()
58
+ end
59
+
60
+ def process_colon2(exp)
61
+ exp.shift # remove :colon2
62
+ @definition.superklass << exp.value
63
+ process_until_empty(exp)
64
+ s()
65
+ end
66
+
67
+ def process_colon3(exp)
68
+ exp.shift # remove :colon3
69
+ @definition.superklass << '::'
70
+ @definition.superklass << exp.value
71
+ process_until_empty(exp)
72
+ s()
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../parsers'
4
+ require_relative '../types'
5
+
6
+ module Xumlidot
7
+ module Parsers
8
+ # Parser for the arguments to a method
9
+ #
10
+ # e.g. formats def method(a, b = nil)
11
+ # to a string 'a, b = nil'
12
+ #
13
+ class MethodSignature < MethodBasedSexpProcessor
14
+
15
+ # Container for values assigned to a variable
16
+ #class Assignments < Hash
17
+ #end
18
+
19
+ attr_reader :definition
20
+
21
+ def initialize(exp, superclass_method = false)
22
+ super()
23
+
24
+ @definition = ::Xumlidot::Types::MethodSignature.new
25
+ @definition.visibility = Scope.get_visibility
26
+ @definition.args = Args.new(exp.dup[0..2]).definition # only pass the method definition into args
27
+ @definition.superclass_method = superclass_method
28
+
29
+ #@assignments = Assignments.new
30
+
31
+ process(exp)
32
+ end
33
+
34
+ def to_s
35
+ @definition.to_s
36
+ end
37
+
38
+ def process_defn(exp)
39
+ exp.shift unless auto_shift_type # node type
40
+ exp.shift if exp.first.is_a?(Sexp) && exp.first.value == :self # remove :self
41
+
42
+ @definition.name = exp.shift
43
+ @definition.file = exp.file
44
+ @definition.line_number = exp.line
45
+ @definition.line_max = exp.line_max
46
+
47
+ more = exp.shift
48
+ process(more) if more.is_a?(Sexp) && !more.empty?
49
+ s()
50
+ rescue Exception => e
51
+ STDERR.puts " ** bug: unable to proces defn #{exp}"
52
+ if ENV["XUMLIDOT_DEBUG"]
53
+ STDERR.puts "ERROR (MethodSignature#process_defn) #{e.message}"
54
+ STDERR.puts e.backtrace.reverse
55
+ end
56
+ s()
57
+ end
58
+
59
+ def process_defs(exp)
60
+ process_defn(exp)
61
+ end
62
+
63
+ # CALLS
64
+ # TODO: We need a seperate assignment class to parse these
65
+ # especially assignments so that we can attempt to work out types
66
+ def process_call(exp)
67
+ exp.shift # remove the :call
68
+
69
+ recv = process(exp.shift) # recv
70
+ name = exp.shift
71
+ args = process(exp.shift) # args
72
+
73
+ exp
74
+ rescue Exception => e
75
+ if ENV["XUMLIDOT_DEBUG"]
76
+ STDERR.puts "ERROR (MethodSignature#process_call) #{e.message}"
77
+ STDERR.puts e.backtrace.reverse
78
+ end
79
+ exp
80
+ end
81
+
82
+ def process_lasgn(exp)
83
+ exp.shift # remove :lasgn
84
+
85
+ name = exp.shift.to_s
86
+ value = exp.shift
87
+
88
+ #@assignments[name] = value
89
+
90
+ process(value)
91
+ s()
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../parsers'
4
+ require_relative '../types'
5
+
6
+ module Xumlidot
7
+ module Parsers
8
+ # Parser for the KLASS DEFINITION ONLY and the name
9
+ # probably should be changed to reflect that
10
+ #
11
+ # The main parser will handle method,
12
+ # constants, etc
13
+ #
14
+ class ModuleDefinition < MethodBasedSexpProcessor
15
+
16
+ attr_reader :definition
17
+
18
+ def initialize(exp, namespace = nil)
19
+ super()
20
+
21
+ @definition = ::Xumlidot::Types::ModuleDefinition.new
22
+ @namespace = namespace.dup
23
+
24
+ process(exp)
25
+ end
26
+
27
+ def process_module(exp)
28
+ exp.shift # remove :module
29
+ definition = exp.shift
30
+
31
+ # Processes the name of the module
32
+ if Sexp === definition
33
+ case definition.sexp_type
34
+ when :colon2 then # Reached in the event that a name is a compound
35
+ name = definition.flatten
36
+ name.delete :const
37
+ name.delete :colon2
38
+ name.each do |v|
39
+ @definition.name << ::Xumlidot::Types::Constant.new(v, @namespace)
40
+ end
41
+ when :colon3 then # Reached in the event that a name begins with ::
42
+ @definition.name << ::Xumlidot::Types::Constant.new(definition.last, '::')
43
+ else
44
+ raise "unknown type #{exp.inspect}"
45
+ end
46
+ else Symbol === definition
47
+ #if we have a symbol we have the actual module name
48
+ # e.g. module Foo; end
49
+ @definition.name << ::Xumlidot::Types::Constant.new(definition, @namespace)
50
+ end
51
+ s()
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../parsers'
4
+ require_relative '../types'
5
+
6
+ module Xumlidot
7
+ module Parsers
8
+
9
+ # Save current visibility and restore it after processing
10
+ module Scope
11
+ # Maintains current state of method visability
12
+ class Visibility
13
+ class << self
14
+
15
+ def state
16
+ @state ||= :public
17
+ end
18
+
19
+ def public
20
+ @state = :public
21
+ end
22
+
23
+ def protected
24
+ @state = :protected
25
+ end
26
+
27
+ def private
28
+ @state = :private
29
+ end
30
+ end
31
+ end
32
+
33
+ def public(&block)
34
+ temp_visibility = get_visibility
35
+ set_visibility
36
+ yield if block_given?
37
+ set_visibility(temp_visibility)
38
+ end
39
+
40
+ def set_visibility(state = :public)
41
+ Visibility.send(state)
42
+ end
43
+
44
+ def get_visibility
45
+ Visibility.state
46
+ end
47
+
48
+ module_function :set_visibility
49
+ module_function :get_visibility
50
+ module_function :public
51
+ end
52
+
53
+ end
54
+ end