sal-tools-analyze 0.0.1
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/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
|