ulg 0.1.0

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.
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: []