ulg 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/bin/ulg +39 -0
  2. data/lib/ulg.rb +245 -0
  3. metadata +47 -0
data/bin/ulg ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ulg'
4
+
5
+ ulg = ULG.new
6
+ opt = Getopt::Std.getopts("f:o:s:dh")
7
+
8
+ def usage
9
+ puts "Usage: ulg.rb [-o OUTPUT] [-s FILE] [-d] FILE FILE..."
10
+ puts "\nReads from stdin if no input FILE given\n\n"
11
+ puts "Other options:\n\n"
12
+ puts " -o OUTPUT Output format (png, dot, svg). Default png"
13
+ puts " -s FILE Save to file"
14
+ puts " -d Debug mode"
15
+ puts " -h This help"
16
+ exit
17
+ end
18
+
19
+ if opt["h"]
20
+ usage
21
+ end
22
+
23
+ if opt["d"]
24
+ ulg.debug
25
+ end
26
+
27
+ if opt["s"]
28
+ ulg.destfile = opt["s"]
29
+ end
30
+
31
+ if opt["o"] and [ "png", "dot", "svg" ].include? opt["o"]
32
+ ulg.output = opt["o"]
33
+ end
34
+
35
+ if ARGV.empty?
36
+ ulg.draw [ "stdin" ]
37
+ else
38
+ ulg.draw ARGV
39
+ end
data/lib/ulg.rb ADDED
@@ -0,0 +1,245 @@
1
+ require "getopt/std"
2
+ require "graphviz"
3
+
4
+ class ULG
5
+
6
+ attr_accessor :destfile, :output
7
+
8
+ def initialize
9
+ @debug = 0
10
+ @destfile = nil
11
+ @output = "png"
12
+
13
+ @read_files = []
14
+
15
+ @graphviz_options = { "type" => "digraph" }
16
+
17
+ @nodes = {}
18
+ @edges = {}
19
+
20
+ @rex = []
21
+
22
+ @rex << '(?<from_open>[\[\<\(\{]*)'
23
+ @rex << '(?<from_label>[^\]\>\)\|\}\=\-\.]+)'
24
+ @rex << '(?<from_color>\|[^\]\>\)\}]+)?'
25
+ @rex << '(?<from_close>[\]\>\)\}]*)\s+'
26
+
27
+ @rex << '(?<from_arrow>[\<\>ox]*)'
28
+
29
+ @rex << '(?<edge_open>[\=\-\.]+)'
30
+ @rex << '(?<edge_label>[^\=\-\|\.]*)'
31
+ @rex << '(?<edge_color>\|[^\=\-\|\.]+)?'
32
+ @rex << '(?<edge_close>[\=\-\.]+)'
33
+
34
+ @rex << '(?<to_arrow>[\<\>ox]*)\s+'
35
+
36
+ @rex << '(?<to_open>[\[\<\(\{]*)'
37
+ @rex << '(?<to_label>[^\]\>\)\|\}\=\-\.]+)'
38
+ @rex << '(?<to_color>\|[^\]\>\)\}]+)?'
39
+ @rex << '(?<to_close>[\]\>\)\}]*)'
40
+
41
+ @arrow_regex = @rex.join('\s*')
42
+ end
43
+
44
+ def debug
45
+ @debug = 1
46
+ end
47
+
48
+ def dputs(msg)
49
+ puts "DEBUG #{msg}" if @debug == 1
50
+ end
51
+
52
+ def wputs(msg)
53
+ puts "WARN #{msg}"
54
+ end
55
+
56
+ def draw(files)
57
+ dputs "Rex: #{@arrow_regex}"
58
+
59
+ files.each do |file|
60
+ parse file
61
+ end
62
+
63
+ if @nodes.size == 0
64
+ wputs "No nodes found so not saving any output"
65
+ return
66
+ end
67
+
68
+ g = GraphViz.new :G, @graphviz_options
69
+
70
+ @edges.keys.each do |from|
71
+ @edges[from].keys.each do |to|
72
+
73
+ from_label = @nodes[from]["label"]
74
+ from_opts = @nodes[from].clone
75
+ from_opts.delete "label"
76
+
77
+ to_label = @nodes[to]["label"]
78
+ to_opts = @nodes[to].clone
79
+ to_opts.delete "label"
80
+
81
+ dputs "Building from node for #{from_label} as #{from}"
82
+ from_node = g.add_nodes from_label, from_opts
83
+ dputs "Building to node for #{to_label} as #{to}"
84
+ to_node = g.add_nodes to_label, to_opts
85
+
86
+ dputs "Building edge for #{from} #{to}"
87
+ g.add_edges from_node, to_node, @edges[from][to]
88
+
89
+ end
90
+ end
91
+
92
+ if not @destfile
93
+ if files.size == 1
94
+ @destfile = "#{files[0]}.#{@output}"
95
+ else
96
+ @destfile = "graph.#{@output}"
97
+ end
98
+ end
99
+
100
+ puts "Saving output to #{@destfile}"
101
+ g.output @output => @destfile
102
+ end
103
+
104
+ def parse(file)
105
+
106
+ puts "Reading #{file}"
107
+ if @read_files.include? file
108
+ wputs "Skipping #{file} as I've seen it before"
109
+ return
110
+ end
111
+
112
+ if file == "stdin"
113
+ contents = STDIN.read
114
+ else
115
+ if File.readable? file
116
+ contents = File.read(file)
117
+ else
118
+ wputs "Cannot read file #{file}, skipping"
119
+ return
120
+ end
121
+ end
122
+
123
+ contents.split("\n").each do |line|
124
+ parse_line line
125
+ end
126
+
127
+ @read_files << file
128
+ end
129
+
130
+ def parse_line(line)
131
+ line.chomp!
132
+
133
+ case line
134
+ when /^\s*option\s+(?<option>\w+)\s+(?<value>\w+)\s*$/i
135
+ set_option $~[:option], $~[:value]
136
+
137
+ when /^\s*include\s+(?<file>.*)$/i
138
+ parse $~[:file]
139
+
140
+ when /^\s*#{@arrow_regex}\s*$/
141
+ from = add_node $~[:from_open], $~[:from_label], $~[:from_color], $~[:from_close]
142
+ to = add_node $~[:to_open], $~[:to_label], $~[:to_color], $~[:to_close]
143
+
144
+ add_edge from, to, $~[:from_arrow], $~[:edge_open], $~[:edge_label], $~[:edge_color], $~[:edge_close], $~[:to_arrow]
145
+ end
146
+ end
147
+
148
+ def set_option(option, value)
149
+ @graphviz_options[option] = value
150
+ end
151
+
152
+ def add_node(open, label, color, close)
153
+
154
+ label.lstrip.rstrip!
155
+ name = label_to_name label
156
+
157
+ if @nodes.has_key? name
158
+ return name
159
+ end
160
+
161
+ dputs "Adding node #{name}"
162
+
163
+ if color
164
+ color = color.gsub /\|+/, ""
165
+ else
166
+ color = "black"
167
+ end
168
+
169
+ case open
170
+ when '<'
171
+ shape = "diamond"
172
+ when '('
173
+ shape = "oval"
174
+ when '{'
175
+ shape = "hexagon"
176
+ else
177
+ shape = "box"
178
+ end
179
+
180
+ @nodes[name] = { "shape" => shape, "label" => label, "fontcolor" => color }
181
+
182
+ return name
183
+ end
184
+
185
+ def add_edge(from, to, from_arrow, open, label, color, close, to_arrow)
186
+
187
+ label.lstrip.rstrip!
188
+
189
+ from_name = label_to_name from
190
+ to_name = label_to_name to
191
+
192
+ return if @edges.has_key? from and @edges[from].has_key? to
193
+
194
+ dputs "Adding edge for #{from_name} #{to_name}"
195
+
196
+ if not @edges.has_key? from_name
197
+ @edges[from_name] = {}
198
+ end
199
+
200
+ arrowtail = parse_arrow from_arrow
201
+ arrowhead = parse_arrow to_arrow
202
+
203
+ case open
204
+ when /^\.+$/
205
+ style = "dotted"
206
+ when /^\-+$/
207
+ style = "dashed"
208
+ else
209
+ style = "solid"
210
+ end
211
+
212
+ if color
213
+ color = color.gsub /\|+/, ""
214
+ else
215
+ color = "black"
216
+ end
217
+
218
+ @edges[from_name][to_name] = { "style" => style, "label" => label, "arrowtail" => arrowtail, "arrowhead" => arrowhead, "fontcolor" => color }
219
+ end
220
+
221
+ def parse_arrow(arrow)
222
+ case arrow
223
+ when '<'
224
+ arrow = "normal"
225
+ when '>'
226
+ arrow = "normal"
227
+ when 'o'
228
+ arrow = "dot"
229
+ when 'x'
230
+ arrow = "box"
231
+ when '<>'
232
+ arrow = "diamond"
233
+ else
234
+ arrow = "none"
235
+ end
236
+
237
+ return arrow
238
+ end
239
+
240
+ def label_to_name(label)
241
+ name = label.gsub /\W+/, ""
242
+ return name.downcase
243
+ end
244
+
245
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ulg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Fraser Scott
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-16 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Text editor friendly markup language for creating simple graphs.
15
+ email: fraser.scott@gmail.com
16
+ executables:
17
+ - ulg
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/ulg.rb
22
+ - bin/ulg
23
+ homepage: http://rubygems.org/gems/ulg
24
+ licenses: []
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 1.8.24
44
+ signing_key:
45
+ specification_version: 3
46
+ summary: Ultra Light Graphs!
47
+ test_files: []