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.
- data/bin/ulg +39 -0
- data/lib/ulg.rb +245 -0
- 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: []
|