sal-tools-analyze 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +1 -0
- data/README +20 -0
- data/lib/sal/class.rb +43 -0
- data/lib/sal/code.rb +236 -0
- data/lib/sal/command.rb +35 -0
- data/lib/sal/external.rb +37 -0
- data/lib/sal/externalfunction.rb +46 -0
- data/lib/sal/format.rb +43 -0
- data/lib/sal/function.rb +26 -0
- data/lib/sal/item.rb +214 -0
- data/lib/sal/library.rb +25 -0
- data/lib/sal/version.rb +161 -0
- data/sal-tools-analyze.gemspec +16 -0
- data/test/data/test.21.text.sql.app +458 -0
- data/test/data/test.40.indented.app +219 -0
- data/test/data/test.40.normal.app +0 -0
- data/test/data/test.40.text.app +218 -0
- data/test/data/test.40.text.classes.app +350 -0
- data/test/data/test.40.text.multiline.app +228 -0
- data/test/data/test.41.indented.app +218 -0
- data/test/data/test.41.normal.app +0 -0
- data/test/data/test.41.text.app +218 -0
- data/test/data/test.41.text.externalfunctions.app +352 -0
- data/test/test.rb +16 -0
- data/test/test_class.rb +34 -0
- data/test/test_code.rb +180 -0
- data/test/test_command.rb +16 -0
- data/test/test_external.rb +37 -0
- data/test/test_externalfunction.rb +76 -0
- data/test/test_format.rb +58 -0
- data/test/test_function.rb +22 -0
- data/test/test_item.rb +123 -0
- data/test/test_library.rb +38 -0
- data/test/test_version.rb +80 -0
- metadata +128 -0
data/AUTHORS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
M. Ehehalt (flynn42ryder@rubyforge.org)
|
data/README
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
= sal-tools - A library for the analyze of SAL code
|
2
|
+
|
3
|
+
== SYNOPSIS
|
4
|
+
|
5
|
+
This library gives the basic functionality to analyse Gupta source code.
|
6
|
+
Copyright (c)2007-2011 by Michael Ehehalt
|
7
|
+
Error and feature list at the end of the document
|
8
|
+
|
9
|
+
== Other stuff
|
10
|
+
|
11
|
+
Author:: M. Ehehalt <flynn42ryder@rubyforge.org>
|
12
|
+
License:: Copyright (C) 2007, 2008, ... M. Ehehalt
|
13
|
+
Released under the GNU GPL 2 license
|
14
|
+
|
15
|
+
== Warranty
|
16
|
+
|
17
|
+
This program is distributed in the hope that it will be useful,
|
18
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
19
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
20
|
+
GNU General Public License for more details.
|
data/lib/sal/class.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative "item.rb"
|
2
|
+
|
3
|
+
module Sal
|
4
|
+
|
5
|
+
# The class repressents a class in the code
|
6
|
+
class Class
|
7
|
+
|
8
|
+
def initialize( item )
|
9
|
+
@item = item
|
10
|
+
item.code.chomp =~ /^(.*? Class): (.*)$/
|
11
|
+
@type = $1
|
12
|
+
@name = $2
|
13
|
+
@functions = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :name, :item, :type
|
17
|
+
|
18
|
+
# Getter for the functions (lazy loading)
|
19
|
+
def functions
|
20
|
+
if( @functions.nil? )
|
21
|
+
_analyze
|
22
|
+
end
|
23
|
+
return @functions
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def _analyze
|
29
|
+
@functions = []
|
30
|
+
|
31
|
+
@item.childs.each do | item |
|
32
|
+
if(item.level == 4 and item.code.start_with? "Functions")
|
33
|
+
item.childs.each do | item |
|
34
|
+
@functions << item if item.code.start_with? "Function"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
data/lib/sal/code.rb
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
require_relative "externalfunction.rb"
|
4
|
+
require_relative "external.rb"
|
5
|
+
require_relative "format.rb"
|
6
|
+
require_relative "item.rb"
|
7
|
+
require_relative "library.rb"
|
8
|
+
require_relative "version.rb"
|
9
|
+
|
10
|
+
module Sal
|
11
|
+
|
12
|
+
# The class code represent the complete code from a sal file.
|
13
|
+
# You can analyze the code now through the items, the libraries, ...
|
14
|
+
# Only code in text format could be analyzed
|
15
|
+
class Code
|
16
|
+
|
17
|
+
# Initialize Code with filename
|
18
|
+
# The sourcecode will read and the format and version are analysed
|
19
|
+
def initialize( filename )
|
20
|
+
read_code_from_file filename
|
21
|
+
get_format_and_version_from_code
|
22
|
+
end
|
23
|
+
|
24
|
+
# Save the items of code back to disk
|
25
|
+
# Without parameters, the original file will be overridden
|
26
|
+
def save( new_filename = filename )
|
27
|
+
save_code_to_file new_filename
|
28
|
+
end
|
29
|
+
|
30
|
+
# Save the items of code back to disk in a new file
|
31
|
+
# Alias for save with a new filename
|
32
|
+
def save_as( new_filename )
|
33
|
+
save_code_to_file new_filename
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_accessor :format, :filename, :version, :code
|
37
|
+
|
38
|
+
# Getter for the items (lazy loading)
|
39
|
+
def items
|
40
|
+
get_items_from_code if @items.nil?
|
41
|
+
@items
|
42
|
+
end
|
43
|
+
|
44
|
+
# Getter for the libraries (lazy loading)
|
45
|
+
def libraries
|
46
|
+
if @libraries.nil?
|
47
|
+
@libraries = Array.new
|
48
|
+
items.each do | item |
|
49
|
+
if( item.level == 2 and !item.parent.nil? and item.parent.code =~ /Libraries/)
|
50
|
+
@libraries << Library.new(item) unless item.commented?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
return @libraries
|
55
|
+
end
|
56
|
+
|
57
|
+
# Add a new library to the libraries section in the code
|
58
|
+
def add_library(filename)
|
59
|
+
# .head 1 + Libraries
|
60
|
+
# .head 2 - File Include: qckttip.apl
|
61
|
+
# .head 2 - File Include: vt.apl
|
62
|
+
items.each do | item |
|
63
|
+
if( item.level == 1 and item.code =~ /Libraries/ )
|
64
|
+
include_item = item.copy
|
65
|
+
include_item.parent = item
|
66
|
+
include_item.childs = []
|
67
|
+
include_item.code = "File Include: #{filename}"
|
68
|
+
include_item.level = item.level + 1
|
69
|
+
@items.insert(@items.index(item) + 1, include_item)
|
70
|
+
item.childs.insert(0, include_item)
|
71
|
+
item.refresh_child_indicator
|
72
|
+
include_item.refresh_child_indicator
|
73
|
+
@libraries = nil
|
74
|
+
return include_item
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Getter for the externals (lazy loading)
|
80
|
+
def externals
|
81
|
+
if @externals.nil?
|
82
|
+
@externals = Array.new
|
83
|
+
items.each do | item |
|
84
|
+
if( item.level == 3 and !item.parent.nil? and item.parent.code =~ /External Functions/ )
|
85
|
+
@externals << External.new(item) unless item.commented?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
return @externals
|
90
|
+
end
|
91
|
+
|
92
|
+
# Getter for the classes (lazy loading)
|
93
|
+
def classes
|
94
|
+
if @classes.nil?
|
95
|
+
@classes = []
|
96
|
+
items.each do | item |
|
97
|
+
if( item.level == 3 and !item.parent.nil? and item.parent.code.start_with?("Class Definitions") )
|
98
|
+
@classes << Class.new(item) unless item.commented?
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
return @classes
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
@file_name
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the current code from the items
|
110
|
+
def generated_code
|
111
|
+
new_code = ""
|
112
|
+
items.each do | item |
|
113
|
+
new_code += item.line
|
114
|
+
end
|
115
|
+
new_code
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a short overview over the code (version, format, ...)
|
119
|
+
def display(smart = true)
|
120
|
+
disp = "#{File.basename @filename} "
|
121
|
+
disp += (smart ? "- " : ":\n")
|
122
|
+
disp += "Version = #{@version.td} "
|
123
|
+
disp += (smart ? "- " : "\n")
|
124
|
+
disp += "Format = #{@format}"
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Saves the current code
|
130
|
+
def save_code_to_file(new_filename)
|
131
|
+
fh = File.new(new_filename, "wb")
|
132
|
+
fh.write( items.join )
|
133
|
+
fh.close
|
134
|
+
end
|
135
|
+
|
136
|
+
# Read the code from the file and sets the filename
|
137
|
+
def read_code_from_file(filename)
|
138
|
+
@filename = filename
|
139
|
+
@code = IO.binread filename
|
140
|
+
end
|
141
|
+
|
142
|
+
# Analyzes the file format (normal, text, indented) and the version
|
143
|
+
def get_format_and_version_from_code
|
144
|
+
@format = Format::get_from_code @code
|
145
|
+
@version = Version::from_code @code
|
146
|
+
end
|
147
|
+
|
148
|
+
# Get the items from the code and generats the @items array
|
149
|
+
def get_items_from_code
|
150
|
+
@items = []
|
151
|
+
case format
|
152
|
+
when Format::TEXT
|
153
|
+
# _analyze_code_textmode
|
154
|
+
analyze_items Format::TEXT
|
155
|
+
when Format::INDENTED
|
156
|
+
# not realized at the moment
|
157
|
+
# _analyze_code_indented
|
158
|
+
# analyze_items Format::INDENTED
|
159
|
+
else
|
160
|
+
# Can't analyze indented and normal mode code
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Analyzes the source code
|
165
|
+
def analyze_items(format)
|
166
|
+
vars = analyze_init(format)
|
167
|
+
get_lines(vars)
|
168
|
+
vars.lines.each do | line |
|
169
|
+
if line.length > 0
|
170
|
+
vars.line = line
|
171
|
+
@items << get_item(vars)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Initialization of the analyze, clears all infos
|
177
|
+
# As parameter communication an open struct is used
|
178
|
+
def analyze_init(format)
|
179
|
+
@items = Array.new
|
180
|
+
vars = OpenStruct.new
|
181
|
+
vars.counter = 0
|
182
|
+
vars.levels = Hash.new
|
183
|
+
vars.format = format
|
184
|
+
return vars
|
185
|
+
end
|
186
|
+
|
187
|
+
# The source code is splitted into lines
|
188
|
+
def get_lines(vars)
|
189
|
+
vars.lines = []
|
190
|
+
if(vars.format == Format::TEXT)
|
191
|
+
code_split_text vars
|
192
|
+
elsif(vars.format == Format::INDENTED)
|
193
|
+
# code_split_indented vars # not realized at the moment
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Split the source code into lines if source code is in text format
|
198
|
+
def code_split_text(vars)
|
199
|
+
lines = @code.split(/^\.head/)
|
200
|
+
lines.each do | line |
|
201
|
+
vars.lines << ".head#{line}" if line.length > 0
|
202
|
+
end
|
203
|
+
vars.lines
|
204
|
+
end
|
205
|
+
|
206
|
+
# Split the source code into lines if source code is in indented format
|
207
|
+
def code_split_indented(vars)
|
208
|
+
vars.lines = @code.split(/\n/)
|
209
|
+
vars.lines.collect { | line | line = line + "\n" }
|
210
|
+
end
|
211
|
+
|
212
|
+
# create the item
|
213
|
+
def get_item(vars)
|
214
|
+
# item = Item.new vars.line, vars.format
|
215
|
+
if(vars.format == Format::TEXT)
|
216
|
+
item = Item.new vars.line, vars.format
|
217
|
+
else
|
218
|
+
item = Item.new(vars.line, Format::INDENTED)
|
219
|
+
end
|
220
|
+
item.code_line_nr = vars.counter + 1
|
221
|
+
vars.levels[item.level] = item
|
222
|
+
if(item.level > 0 and vars.levels.count > 1)
|
223
|
+
begin
|
224
|
+
item.parent = vars.levels[item.level-1]
|
225
|
+
item.parent.childs << item
|
226
|
+
rescue
|
227
|
+
warn "filename = #{@filename}"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
vars.counter += 1 # Bei Textmode war vorher: item_counter = item_counter + code_line.split('\n').length
|
231
|
+
item
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
data/lib/sal/command.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Sal
|
2
|
+
|
3
|
+
# Checks if the code line to test is a line of code or other stuff (window parameters, ...)
|
4
|
+
module Command
|
5
|
+
|
6
|
+
@@commands = {
|
7
|
+
:break => "Break",
|
8
|
+
:call => "Call",
|
9
|
+
:case => "Case", # => Select Case
|
10
|
+
:default => "Default", # => Select Case
|
11
|
+
:else => "Else",
|
12
|
+
:else_if => "Else If",
|
13
|
+
:if => "If",
|
14
|
+
:loop => "Loop",
|
15
|
+
:on => "On",
|
16
|
+
:return => "Return",
|
17
|
+
:select_case => "Select Case",
|
18
|
+
:set => "Set",
|
19
|
+
:when => "When",
|
20
|
+
:while => "While"
|
21
|
+
}
|
22
|
+
|
23
|
+
# Checks if the line of code is code or other stuff
|
24
|
+
# with using of the @@commands array, that includes
|
25
|
+
# the source code starts
|
26
|
+
def Command.is_code_line?(line)
|
27
|
+
@@commands.each_value do | command |
|
28
|
+
return true if line.start_with? "#{command} "
|
29
|
+
end
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/sal/external.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative "item.rb"
|
2
|
+
|
3
|
+
module Sal
|
4
|
+
|
5
|
+
# The class represent an included external library (dll)
|
6
|
+
class External
|
7
|
+
|
8
|
+
def initialize( item )
|
9
|
+
@item = item
|
10
|
+
@name = item.code.gsub(/Library name: /,"")
|
11
|
+
@functions = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :name, :item
|
15
|
+
|
16
|
+
# Getter of the dll functions (lazy loading)
|
17
|
+
def functions
|
18
|
+
if( @functions.nil? )
|
19
|
+
_analyze
|
20
|
+
end
|
21
|
+
return @functions
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def _analyze
|
27
|
+
@functions = Array.new
|
28
|
+
@item.childs.each do | item |
|
29
|
+
if item.code =~ /Function: /
|
30
|
+
@functions << ExternalFunction.new( self, item )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "item.rb"
|
2
|
+
require_relative "external.rb"
|
3
|
+
|
4
|
+
module Sal
|
5
|
+
|
6
|
+
# This class represent an external function as chilf from the external
|
7
|
+
# library (dll)
|
8
|
+
class ExternalFunction
|
9
|
+
|
10
|
+
def initialize( external, item )
|
11
|
+
@external = external
|
12
|
+
@item = item
|
13
|
+
@name = nil
|
14
|
+
@ordinal = -1
|
15
|
+
@parameters = Array.new
|
16
|
+
|
17
|
+
#@appears = 0
|
18
|
+
#@defs = Array.new
|
19
|
+
#@calls = Hash.new # hash with files whith a child code line array
|
20
|
+
|
21
|
+
_analyze
|
22
|
+
@key = @external.name + "::" + @name
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :external, :item , :name, :ordinal, :key, :parameters # :appears, :defs, :calls
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def _analyze
|
30
|
+
@name = $1 if @item.code =~ /Function: (.*)$/
|
31
|
+
@item.childs.each do | child |
|
32
|
+
# ordinal number
|
33
|
+
@ordinal = $1.to_i if child.code =~ /Export Ordinal: (\d+)/
|
34
|
+
|
35
|
+
# parameter
|
36
|
+
if child.level == 5 and child.code =~ /Parameters/
|
37
|
+
child.childs.each do | parameter |
|
38
|
+
@parameters << parameter unless parameter.commented?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/lib/sal/format.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Sal
|
2
|
+
|
3
|
+
# Analyzes the file format from the sal sourcecode
|
4
|
+
# The result is one of the constantsTEXT, NORMAL or INDENTED.
|
5
|
+
module Format
|
6
|
+
|
7
|
+
TEXT = :t
|
8
|
+
NORMAL = :n
|
9
|
+
INDENTED = :i
|
10
|
+
|
11
|
+
# Analyzes the file format for the given chunk of code
|
12
|
+
def Format.get_from_code( code )
|
13
|
+
if(code =~ /Outline Version - \d\.\d\.(\d\d)/m)
|
14
|
+
if(code =~ /^\.head/)
|
15
|
+
return Format::TEXT
|
16
|
+
else
|
17
|
+
return Format::INDENTED
|
18
|
+
end
|
19
|
+
else
|
20
|
+
return Format::NORMAL
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Analyzes the file format for the file with the given filename
|
25
|
+
# The file is read and the source code sends to the method get_from_code
|
26
|
+
def Format.get_from_file( filename )
|
27
|
+
code = IO.binread(filename)
|
28
|
+
return get_from_code( code )
|
29
|
+
end
|
30
|
+
|
31
|
+
def Format.get_from_line( line )
|
32
|
+
if(line.start_with? ".head")
|
33
|
+
return Format::TEXT
|
34
|
+
elsif(line.start_with? "Application Description" or line.start_with? "\t")
|
35
|
+
return Format::INDENTED
|
36
|
+
else
|
37
|
+
return Format::NORMAL
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|