copy-expander 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +6 -0
- data/Gemspec +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +92 -0
- data/Rakefile +30 -0
- data/bin/expander +6 -0
- data/cobol-source/COPY1LVL.CBL +16 -0
- data/cobol-source/COPY1LVLA.CBL +18 -0
- data/cobol-source/COPY2LVL.CBL +17 -0
- data/cobol-source/COPY3LVL.CBL +17 -0
- data/cobol-source/NOCOPY.CBL +16 -0
- data/cobol-source/copy/COPY1 +4 -0
- data/cobol-source/copy/COPY1A +4 -0
- data/cobol-source/copy/COPY2 +8 -0
- data/cobol-source/copy/COPY3 +12 -0
- data/copy-expander.gemspec +24 -0
- data/expander.yml +5 -0
- data/expected/EXPECTED1.CBL +19 -0
- data/expected/EXPECTED2.CBL +24 -0
- data/expected/EXPECTED3.CBL +27 -0
- data/expected/EXPECTED4.CBL +41 -0
- data/lib/copy-expander/version.rb +3 -0
- data/lib/copy_expander.rb +102 -0
- data/lib/copy_statement.rb +46 -0
- data/lib/expander.rb +66 -0
- data/lib/expander_config.rb +27 -0
- data/run-specs +1 -0
- data/spec/lib/copy-expander/copy_expander_spec.rb +154 -0
- data/spec/lib/copy-expander/copy_statement_spec.rb +57 -0
- data/spec/lib/copy-expander/expander_config_spec.rb +33 -0
- data/spec/lib/copy-expander/expander_functional.rb +49 -0
- data/spec/lib/copy-expander/expander_spec.rb +28 -0
- data/spec/lib/copy-expander/snippets.txt +14 -0
- data/spec/spec_helper.rb +3 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2fbe6c5515216267422bea2aa60dd219255297dd
|
4
|
+
data.tar.gz: 245721ae907ee85ba3926a355cca7eaf9123db98
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 68d368ebcde68b7231e5ea6b534080c83aa66c4ac369d7be2b62cf3feadcd7d5591373d2e5ac8f2995308e2efc805951d85c5e8fff4a5f7eabea40bce444a827
|
7
|
+
data.tar.gz: 96724b3656176cb931044bda19bd1d2863a9a47b2faddbc0aac3af31f01b99475f18ab855ea9bb5628ce1e084d6070b8b94f856cf0be4048891680c3f1fb6b7d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemspec
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Dave Nicolette
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# copy-expander
|
2
|
+
|
3
|
+
Utility to expand nested COPY REPLACING statements in a COBOL source program.
|
4
|
+
|
5
|
+
## The problem
|
6
|
+
|
7
|
+
The COBOL specification does not allow nested COPY REPLACING statements because of the risk of recursion. However, IBM implementations of COBOL attempt to determine whether recursion would _actually_ occur, and if not then they allow the nesting. When we want to use an off-platform COBOL tool such as GnuCOBOL to work with legacy mainframe code, programs that use this technique will (probably) not compile.
|
8
|
+
|
9
|
+
Nested COPY REPLACING is not a generally-accepted good practice for COBOL development, but we have to be realistic about handling legacy code that is found in the wild.
|
10
|
+
|
11
|
+
## The solution
|
12
|
+
|
13
|
+
The solution (or _workaround_, if you prefer) is to expand the nested copybooks and apply the replacements before executing the compiler. This utility performs the expansion and text replacement. It does not automatically compile the resulting program.
|
14
|
+
|
15
|
+
## Configuration
|
16
|
+
|
17
|
+
Define the configuration options in ```expander.yml```. The configuration file lives in the project root directory. Here is a sample:
|
18
|
+
|
19
|
+
```yml
|
20
|
+
{
|
21
|
+
expanded_file: TEMP.CBL,
|
22
|
+
source_dir: cobol-source,
|
23
|
+
copy_dir: cobol-source/copy
|
24
|
+
}
|
25
|
+
```
|
26
|
+
|
27
|
+
Here is an example that complies with the default directory structure used in the [cobol-unit-test](http://github.com/neopragma/cobol-unit-test) project:
|
28
|
+
|
29
|
+
```yml
|
30
|
+
{
|
31
|
+
expanded_file: TEMP.CBL,
|
32
|
+
source_dir: src/main/cobol,
|
33
|
+
copy_dir: src/main/cobol/copy
|
34
|
+
}
|
35
|
+
```
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
Run the utility from the command line, passing the name of the source program as the first command-line argument:
|
40
|
+
|
41
|
+
```shell
|
42
|
+
expander PROGNAME.CBL
|
43
|
+
```
|
44
|
+
|
45
|
+
The source file with the copybooks expanded will be written to the file refernced by the ```expanded-file``` key in the ```expander.yml``` file. In this example, it will be ```./TEMP.CBL```.
|
46
|
+
|
47
|
+
## Running the checks
|
48
|
+
|
49
|
+
Rake and rspec are used for automated checks.
|
50
|
+
|
51
|
+
```shell
|
52
|
+
rake help for expander:
|
53
|
+
rake help => this text
|
54
|
+
rake fast => run specs not tagged slow (default)
|
55
|
+
rake slow => run specs tagged slow
|
56
|
+
rake all => run all specs
|
57
|
+
rake functional => run functional checks (no mocks)
|
58
|
+
```
|
59
|
+
|
60
|
+
## Installation
|
61
|
+
|
62
|
+
Add this line to your application's Gemfile:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
gem 'copy-expander'
|
66
|
+
```
|
67
|
+
|
68
|
+
And then execute:
|
69
|
+
|
70
|
+
$ bundle
|
71
|
+
|
72
|
+
or install it yourself as:
|
73
|
+
|
74
|
+
$ gem install copy-expander
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
1. Fork it ( https://github.com/[my-github-username]/copy-expander/fork )
|
79
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
80
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
81
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
82
|
+
5. Create a new Pull Request
|
83
|
+
|
84
|
+
Specs, comments, and documentation must be updated and all specs passing for a pull request to be deemed "complete."
|
85
|
+
|
86
|
+
## Issues
|
87
|
+
|
88
|
+
Use Github's issue tracking system.
|
89
|
+
|
90
|
+
## License
|
91
|
+
|
92
|
+
MIT.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:fast) do |t|
|
5
|
+
puts 'running fast specs...'
|
6
|
+
t.rspec_opts = '--tag ~slow'
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:slow) do |t|
|
10
|
+
puts 'running slow specs...'
|
11
|
+
t.pattern = [ "spec/**/*_spec.rb" ]
|
12
|
+
t.rspec_opts = '--tag slow'
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec::Core::RakeTask.new(:functional) do |t|
|
16
|
+
puts 'running functional specs...'
|
17
|
+
t.pattern = [ "spec/**/*_functional.rb" ]
|
18
|
+
end
|
19
|
+
|
20
|
+
task(:help) do |t|
|
21
|
+
puts 'rake help for expander:'
|
22
|
+
puts 'rake help => this text'
|
23
|
+
puts 'rake fast => run specs not tagged slow (default)'
|
24
|
+
puts 'rake slow => run specs tagged slow'
|
25
|
+
puts 'rake all => run all specs'
|
26
|
+
puts 'rake functional => run functional checks (no mocks)'
|
27
|
+
end
|
28
|
+
|
29
|
+
task :default => :fast
|
30
|
+
task :all => [ :fast, :slow, :functional ]
|
data/bin/expander
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
000010 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000020 PROGRAM-NAME. COPY1LVL. AAAAAAAA
|
3
|
+
000030 ENVIRONMENT DIVISION. AAAAAAAA
|
4
|
+
000040 DATA DIVISION. AAAAAAAA
|
5
|
+
000050 WORKING-STORAGE SECTION. AAAAAAAA
|
6
|
+
000060 01 WS-FOO. AAAAAAAA
|
7
|
+
000070 05 WS-BAR PIC X(05). AAAAAAAA
|
8
|
+
000080 05 WS-BAZ PIC X(10). AAAAAAAA
|
9
|
+
000090 COPY COPY1 REPLACING ==:PREFIX:== BY ==WS==. AAAAAAAA
|
10
|
+
000100 01 WS-BLAH. AAAAAAAA
|
11
|
+
000110 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
12
|
+
000120 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
13
|
+
000130 PROCEDURE DIVISION. AAAAAAAA
|
14
|
+
000140 DISPLAY 'This program contains COPY statements 1 level deep.'AAAAAAAA
|
15
|
+
000150 GOBACK AAAAAAAA
|
16
|
+
000160 . AAAAAAAA
|
@@ -0,0 +1,18 @@
|
|
1
|
+
000010 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000020 PROGRAM-NAME. COPY1LVLA. AAAAAAAA
|
3
|
+
000030 ENVIRONMENT DIVISION. AAAAAAAA
|
4
|
+
000040 DATA DIVISION. AAAAAAAA
|
5
|
+
000050 WORKING-STORAGE SECTION. AAAAAAAA
|
6
|
+
000060 01 WS-FOO. AAAAAAAA
|
7
|
+
000070 05 WS-BAR PIC X(05). AAAAAAAA
|
8
|
+
000080 05 WS-BAZ PIC X(10). AAAAAAAA
|
9
|
+
000090 COPY COPY1 REPLACING ==:PREFIX:== BY ==WS1==. AAAAAAAA
|
10
|
+
000100 COPY COPY1A REPLACING ==:PREFIX:== BY ==WS2==. AAAAAAAA
|
11
|
+
000110 01 WS-BLAH. AAAAAAAA
|
12
|
+
000120 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
13
|
+
000130 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
14
|
+
000140 PROCEDURE DIVISION. AAAAAAAA
|
15
|
+
000150 DISPLAY 'This program contains 2 COPY statements ' AAAAAAAA
|
16
|
+
000160 '1 LEVEL DEEP.' AAAAAAAA
|
17
|
+
000170 GOBACK AAAAAAAA
|
18
|
+
000180 . AAAAAAAA
|
@@ -0,0 +1,17 @@
|
|
1
|
+
000010 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000020 PROGRAM-NAME. COPY2LVL. AAAAAAAA
|
3
|
+
000030 ENVIRONMENT DIVISION. AAAAAAAA
|
4
|
+
000040 DATA DIVISION. AAAAAAAA
|
5
|
+
000050 WORKING-STORAGE SECTION. AAAAAAAA
|
6
|
+
000060 01 WS-FOO. AAAAAAAA
|
7
|
+
000070 05 WS-BAR PIC X(05). AAAAAAAA
|
8
|
+
000080 05 WS-BAZ PIC X(10). AAAAAAAA
|
9
|
+
000090 COPY COPY2 REPLACING ==:PREFIX:== BY ==ONE-DEEP==. AAAAAAAA
|
10
|
+
000100 01 WS-BLAH. AAAAAAAA
|
11
|
+
000110 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
12
|
+
000120 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
13
|
+
000130 PROCEDURE DIVISION. AAAAAAAA
|
14
|
+
000140 DISPLAY 'This program contains COPY statements ' AAAAAAAA
|
15
|
+
000150 '2 LEVELS DEEP.' AAAAAAAA
|
16
|
+
000160 GOBACK AAAAAAAA
|
17
|
+
000170 . AAAAAAAA
|
@@ -0,0 +1,17 @@
|
|
1
|
+
000010 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000020 PROGRAM-NAME. COPY3LVL. AAAAAAAA
|
3
|
+
000030 ENVIRONMENT DIVISION. AAAAAAAA
|
4
|
+
000040 DATA DIVISION. AAAAAAAA
|
5
|
+
000050 WORKING-STORAGE SECTION. AAAAAAAA
|
6
|
+
000060 01 WS-FOO. AAAAAAAA
|
7
|
+
000070 05 WS-BAR PIC X(05). AAAAAAAA
|
8
|
+
000080 05 WS-BAZ PIC X(10). AAAAAAAA
|
9
|
+
000090 COPY COPY3 REPLACING ==:PREFIX:== BY ==THREE-DEEP==. AAAAAAAA
|
10
|
+
000100 01 WS-BLAH. AAAAAAAA
|
11
|
+
000110 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
12
|
+
000120 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
13
|
+
000130 PROCEDURE DIVISION. AAAAAAAA
|
14
|
+
000140 DISPLAY 'This program contains COPY statements ' AAAAAAAA
|
15
|
+
000150 '3 LEVELS DEEP.' AAAAAAAA
|
16
|
+
000160 GOBACK AAAAAAAA
|
17
|
+
000170 . AAAAAAAA
|
@@ -0,0 +1,16 @@
|
|
1
|
+
000001 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000002* This is a comment AAAAAAAA
|
3
|
+
000003 PROGRAM-NAME. NOCOPY. AAAAAAAA
|
4
|
+
000004 ENVIRONMENT DIVISION. AAAAAAAA
|
5
|
+
000005 DATA DIVISION. AAAAAAAA
|
6
|
+
000006 WORKING-STORAGE SECTION. AAAAAAAA
|
7
|
+
000007 01 WS-FOO. AAAAAAAA
|
8
|
+
000008 05 WS-BAR PIC X(05). AAAAAAAA
|
9
|
+
000009 05 WS-BAZ PIC X(10). AAAAAAAA
|
10
|
+
000010 01 WS-BLAH. AAAAAAAA
|
11
|
+
000011 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
12
|
+
000012 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
13
|
+
000013 PROCEDURE DIVISION. AAAAAAAA
|
14
|
+
000014 DISPLAY 'This program contains no COPY statements.' AAAAAAAA
|
15
|
+
000015 GOBACK AAAAAAAA
|
16
|
+
000016 . AAAAAAAA
|
@@ -0,0 +1,8 @@
|
|
1
|
+
000010* Copybook with nested COPY statement AAAAAAAA
|
2
|
+
000020 01 :PREFIX:-COPY2-GROUP. AAAAAAAA
|
3
|
+
000030 05 :PREFIX:-COPY2-ITEM1 PIC X. AAAAAAAA
|
4
|
+
000040 05 :PREFIX:-COPY2-ITEM2 PIC X. AAAAAAAA
|
5
|
+
000050 COPY COPY1 REPLACING ==:PREFIX:== BY ==TWO-DEEP== AAAAAAAA
|
6
|
+
000060 01 :PREFIX:-COPY2-GROUP2. AAAAAAAA
|
7
|
+
000070 05 :PREFIX:-COPY2-ITEM1 PIC X. AAAAAAAA
|
8
|
+
000080 05 :PREFIX:-COPY2-ITEM2 PIC X. AAAAAAAA
|
@@ -0,0 +1,12 @@
|
|
1
|
+
000010* Copybook with nested COPY statements (3 deep) AAAAAAAA
|
2
|
+
000020 01 :PREFIX:-COPY3-GROUP. AAAAAAAA
|
3
|
+
000030 05 :PREFIX:-COPY3-ITEM1 PIC X. AAAAAAAA
|
4
|
+
000040 05 :PREFIX:-COPY3-ITEM2 PIC X. AAAAAAAA
|
5
|
+
000050 COPY COPY2 REPLACING ==:PREFIX:== BY ==TWO-DEEP== AAAAAAAA
|
6
|
+
000060 01 :PREFIX:-COPY3-GROUP2. AAAAAAAA
|
7
|
+
000070 05 :PREFIX:-COPY3-ITEM1 PIC X. AAAAAAAA
|
8
|
+
000080 05 :PREFIX:-COPY3-ITEM2 PIC X. AAAAAAAA
|
9
|
+
000050 COPY COPY1 REPLACING ==:PREFIX:== BY ==ANOTHER== AAAAAAAA
|
10
|
+
000060 01 :PREFIX:-COPY3-GROUP3. AAAAAAAA
|
11
|
+
000070 05 :PREFIX:-COPY3-ITEM3 PIC X. AAAAAAAA
|
12
|
+
000080 05 :PREFIX:-COPY3-ITEM4 PIC X. AAAAAAAA
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'copy-expander/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "copy-expander"
|
8
|
+
spec.version = Dfhmdf::VERSION
|
9
|
+
spec.authors = ["Dave Nicolette"]
|
10
|
+
spec.email = ["davenicolette@gmail.com"]
|
11
|
+
spec.summary = %q{Expands nested COBOL COPY REPLACING statements}
|
12
|
+
spec.description = %q{Expands nested COBOL COPY REPLACING statements}
|
13
|
+
spec.homepage = "http://github.com/neopragma/copy-expander"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rubocop"
|
24
|
+
end
|
data/expander.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
000010 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000020 PROGRAM-NAME. COPY1LVL. AAAAAAAA
|
3
|
+
000030 ENVIRONMENT DIVISION. AAAAAAAA
|
4
|
+
000040 DATA DIVISION. AAAAAAAA
|
5
|
+
000050 WORKING-STORAGE SECTION. AAAAAAAA
|
6
|
+
000060 01 WS-FOO. AAAAAAAA
|
7
|
+
000070 05 WS-BAR PIC X(05). AAAAAAAA
|
8
|
+
000080 05 WS-BAZ PIC X(10). AAAAAAAA
|
9
|
+
000010* Copybook with no nested COPY statements AAAAAAAA
|
10
|
+
000020 01 WS-COPY1-GROUP. AAAAAAAA
|
11
|
+
000030 05 WS-COPY1-ITEM1 PIC X. AAAAAAAA
|
12
|
+
000040 05 WS-COPY1-ITEM2 PIC X. AAAAAAAA
|
13
|
+
000100 01 WS-BLAH. AAAAAAAA
|
14
|
+
000110 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
15
|
+
000120 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
16
|
+
000130 PROCEDURE DIVISION. AAAAAAAA
|
17
|
+
000140 DISPLAY 'This program contains COPY statements 1 level deep.'AAAAAAAA
|
18
|
+
000150 GOBACK AAAAAAAA
|
19
|
+
000160 . AAAAAAAA
|
@@ -0,0 +1,24 @@
|
|
1
|
+
000010 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000020 PROGRAM-NAME. COPY1LVLA. AAAAAAAA
|
3
|
+
000030 ENVIRONMENT DIVISION. AAAAAAAA
|
4
|
+
000040 DATA DIVISION. AAAAAAAA
|
5
|
+
000050 WORKING-STORAGE SECTION. AAAAAAAA
|
6
|
+
000060 01 WS-FOO. AAAAAAAA
|
7
|
+
000070 05 WS-BAR PIC X(05). AAAAAAAA
|
8
|
+
000080 05 WS-BAZ PIC X(10). AAAAAAAA
|
9
|
+
000010* Copybook with no nested COPY statements AAAAAAAA
|
10
|
+
000020 01 WS1-COPY1-GROUP. AAAAAAAA
|
11
|
+
000030 05 WS1-COPY1-ITEM1 PIC X. AAAAAAAA
|
12
|
+
000040 05 WS1-COPY1-ITEM2 PIC X. AAAAAAAA
|
13
|
+
000010* Copybook with no nested COPY statements AAAAAAAA
|
14
|
+
000020 01 WS2-COPY1A-GROUP. AAAAAAAA
|
15
|
+
000030 05 WS2-COPY1A-ITEM1 PIC X. AAAAAAAA
|
16
|
+
000040 05 WS2-COPY1A-ITEM2 PIC X. AAAAAAAA
|
17
|
+
000110 01 WS-BLAH. AAAAAAAA
|
18
|
+
000120 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
19
|
+
000130 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
20
|
+
000140 PROCEDURE DIVISION. AAAAAAAA
|
21
|
+
000150 DISPLAY 'This program contains 2 COPY statements ' AAAAAAAA
|
22
|
+
000160 '1 LEVEL DEEP.' AAAAAAAA
|
23
|
+
000170 GOBACK AAAAAAAA
|
24
|
+
000180 . AAAAAAAA
|
@@ -0,0 +1,27 @@
|
|
1
|
+
000010 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000020 PROGRAM-NAME. COPY2LVL. AAAAAAAA
|
3
|
+
000030 ENVIRONMENT DIVISION. AAAAAAAA
|
4
|
+
000040 DATA DIVISION. AAAAAAAA
|
5
|
+
000050 WORKING-STORAGE SECTION. AAAAAAAA
|
6
|
+
000060 01 WS-FOO. AAAAAAAA
|
7
|
+
000070 05 WS-BAR PIC X(05). AAAAAAAA
|
8
|
+
000080 05 WS-BAZ PIC X(10). AAAAAAAA
|
9
|
+
000010* Copybook with nested COPY statement AAAAAAAA
|
10
|
+
000020 01 ONE-DEEP-COPY2-GROUP. AAAAAAAA
|
11
|
+
000030 05 ONE-DEEP-COPY2-ITEM1 PIC X. AAAAAAAA
|
12
|
+
000040 05 ONE-DEEP-COPY2-ITEM2 PIC X. AAAAAAAA
|
13
|
+
000010* Copybook with no nested COPY statements AAAAAAAA
|
14
|
+
000020 01 TWO-DEEP-COPY1-GROUP. AAAAAAAA
|
15
|
+
000030 05 TWO-DEEP-COPY1-ITEM1 PIC X. AAAAAAAA
|
16
|
+
000040 05 TWO-DEEP-COPY1-ITEM2 PIC X. AAAAAAAA
|
17
|
+
000060 01 ONE-DEEP-COPY2-GROUP2. AAAAAAAA
|
18
|
+
000070 05 ONE-DEEP-COPY2-ITEM1 PIC X. AAAAAAAA
|
19
|
+
000080 05 ONE-DEEP-COPY2-ITEM2 PIC X. AAAAAAAA
|
20
|
+
000100 01 WS-BLAH. AAAAAAAA
|
21
|
+
000110 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
22
|
+
000120 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
23
|
+
000130 PROCEDURE DIVISION. AAAAAAAA
|
24
|
+
000140 DISPLAY 'This program contains COPY statements ' AAAAAAAA
|
25
|
+
000150 '2 LEVELS DEEP.' AAAAAAAA
|
26
|
+
000160 GOBACK AAAAAAAA
|
27
|
+
000170 . AAAAAAAA
|
@@ -0,0 +1,41 @@
|
|
1
|
+
000010 IDENTIFICATION DIVISION. AAAAAAAA
|
2
|
+
000020 PROGRAM-NAME. COPY3LVL. AAAAAAAA
|
3
|
+
000030 ENVIRONMENT DIVISION. AAAAAAAA
|
4
|
+
000040 DATA DIVISION. AAAAAAAA
|
5
|
+
000050 WORKING-STORAGE SECTION. AAAAAAAA
|
6
|
+
000060 01 WS-FOO. AAAAAAAA
|
7
|
+
000070 05 WS-BAR PIC X(05). AAAAAAAA
|
8
|
+
000080 05 WS-BAZ PIC X(10). AAAAAAAA
|
9
|
+
000010* Copybook with nested COPY statements (3 deep) AAAAAAAA
|
10
|
+
000020 01 THREE-DEEP-COPY3-GROUP. AAAAAAAA
|
11
|
+
000030 05 THREE-DEEP-COPY3-ITEM1 PIC X. AAAAAAAA
|
12
|
+
000040 05 THREE-DEEP-COPY3-ITEM2 PIC X. AAAAAAAA
|
13
|
+
000010* Copybook with nested COPY statement AAAAAAAA
|
14
|
+
000020 01 TWO-DEEP-COPY2-GROUP. AAAAAAAA
|
15
|
+
000030 05 TWO-DEEP-COPY2-ITEM1 PIC X. AAAAAAAA
|
16
|
+
000040 05 TWO-DEEP-COPY2-ITEM2 PIC X. AAAAAAAA
|
17
|
+
000010* Copybook with no nested COPY statements AAAAAAAA
|
18
|
+
000020 01 TWO-DEEP-COPY1-GROUP. AAAAAAAA
|
19
|
+
000030 05 TWO-DEEP-COPY1-ITEM1 PIC X. AAAAAAAA
|
20
|
+
000040 05 TWO-DEEP-COPY1-ITEM2 PIC X. AAAAAAAA
|
21
|
+
000060 01 TWO-DEEP-COPY2-GROUP2. AAAAAAAA
|
22
|
+
000070 05 TWO-DEEP-COPY2-ITEM1 PIC X. AAAAAAAA
|
23
|
+
000080 05 TWO-DEEP-COPY2-ITEM2 PIC X. AAAAAAAA
|
24
|
+
000060 01 THREE-DEEP-COPY3-GROUP2. AAAAAAAA
|
25
|
+
000070 05 THREE-DEEP-COPY3-ITEM1 PIC X. AAAAAAAA
|
26
|
+
000080 05 THREE-DEEP-COPY3-ITEM2 PIC X. AAAAAAAA
|
27
|
+
000010* Copybook with no nested COPY statements AAAAAAAA
|
28
|
+
000020 01 ANOTHER-COPY1-GROUP. AAAAAAAA
|
29
|
+
000030 05 ANOTHER-COPY1-ITEM1 PIC X. AAAAAAAA
|
30
|
+
000040 05 ANOTHER-COPY1-ITEM2 PIC X. AAAAAAAA
|
31
|
+
000060 01 THREE-DEEP-COPY3-GROUP3. AAAAAAAA
|
32
|
+
000070 05 THREE-DEEP-COPY3-ITEM3 PIC X. AAAAAAAA
|
33
|
+
000080 05 THREE-DEEP-COPY3-ITEM4 PIC X. AAAAAAAA
|
34
|
+
000100 01 WS-BLAH. AAAAAAAA
|
35
|
+
000110 05 WS-BLAH-BLAH PIC X. AAAAAAAA
|
36
|
+
000120 05 WS-BLAH-BLAH-BLAH PIC X. AAAAAAAA
|
37
|
+
000130 PROCEDURE DIVISION. AAAAAAAA
|
38
|
+
000140 DISPLAY 'This program contains COPY statements ' AAAAAAAA
|
39
|
+
000150 '3 LEVELS DEEP.' AAAAAAAA
|
40
|
+
000160 GOBACK AAAAAAAA
|
41
|
+
000170 . AAAAAAAA
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require_relative 'copy_statement'
|
2
|
+
require_relative '../lib/expander_config'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Work with individual source lines to perform token replacement declared
|
6
|
+
# in COBOL COPY REPLACING statements.
|
7
|
+
#
|
8
|
+
module CopyExpander
|
9
|
+
include ExpanderConfig
|
10
|
+
|
11
|
+
|
12
|
+
def init
|
13
|
+
@copy_statement_count = 0
|
14
|
+
@copy_statement_depth = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def copy_statement_count
|
18
|
+
@copy_statement_count
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Expand COPY statement found on a single line of COBOL source code.
|
23
|
+
#
|
24
|
+
def process line
|
25
|
+
return line if comment? line
|
26
|
+
break_up_source_line line
|
27
|
+
return line unless has_copy_statement?
|
28
|
+
@copy_statement_count += 1
|
29
|
+
expand_copybook CopyStatement.new(@work_area)
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Recursively expand copybooks and replace tokens
|
34
|
+
#
|
35
|
+
def expand_copybook copy_statement
|
36
|
+
copybook = File.open("#{copy_dir}/#{copy_statement.copybook_name}", 'r')
|
37
|
+
begin
|
38
|
+
line = copybook.readline
|
39
|
+
if comment? line
|
40
|
+
write_from line
|
41
|
+
else
|
42
|
+
break_up_source_line line
|
43
|
+
if has_copy_statement?
|
44
|
+
@copy_statement_count += 1
|
45
|
+
expand_copybook CopyStatement.new(@work_area)
|
46
|
+
else
|
47
|
+
@work_area = replace_tokens(@work_area, copy_statement)
|
48
|
+
write_from reconstruct_line
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end while copybook.eof? == false
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Save the first six and last eight characters of the 80-character line
|
56
|
+
# so that we won't shift characters into the interpreted area of the
|
57
|
+
# line when we make text substitutions.
|
58
|
+
#
|
59
|
+
def break_up_source_line line
|
60
|
+
line.length >= 6 ? @first_six_characters = line[0..5] : nil
|
61
|
+
line.length >= 80 ? @last_eight_characters = line[72..79] : nil
|
62
|
+
line.length >= 72 ? @work_area = line[6..71] : nil
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Is this line a comment?
|
67
|
+
# Source comments in COBOL are identified by an asterisk in position 7.
|
68
|
+
#
|
69
|
+
def comment? line
|
70
|
+
line[6] == '*'
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Does the working area of the source line contain a COPY statement?
|
75
|
+
#
|
76
|
+
def has_copy_statement?
|
77
|
+
@work_area.match(/ COPY (?=([^\"\']*\"[^\"\']*\"\')*[^\"\']*$)/i) ? true : false
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Reconstruct the original source line.
|
82
|
+
#
|
83
|
+
def reconstruct_line
|
84
|
+
@first_six_characters + ('%-66.66s' % @work_area) + @last_eight_characters + "\n"
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Make the current source line a comment line.
|
89
|
+
#
|
90
|
+
def commentize line
|
91
|
+
line[6] = '*'
|
92
|
+
line
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Carry out token replacement in a source line per the REPLACING option
|
97
|
+
#
|
98
|
+
def replace_tokens line, copy_statement
|
99
|
+
line.gsub(copy_statement.old_value, copy_statement.new_value)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
##
|
2
|
+
# Encapsulate the details of a COBOL COPY statement.
|
3
|
+
#
|
4
|
+
class CopyStatement
|
5
|
+
|
6
|
+
##
|
7
|
+
# Parse out the key elements of a COPY statement.
|
8
|
+
#
|
9
|
+
def initialize line
|
10
|
+
tokens = line.gsub(/\s+/, ' ').split
|
11
|
+
last = tokens.length-1
|
12
|
+
@has_period = tokens[last].match(/\.$/) == nil ? false : true
|
13
|
+
tokens[last] = tokens[last].gsub(/\./, '')
|
14
|
+
@copybook_name = tokens[1]
|
15
|
+
@has_replacing = tokens.length > 2 && tokens[2].downcase == 'replacing' ? true : false
|
16
|
+
@old_value = strip_equals_signs(tokens[3]) if has_replacing?
|
17
|
+
@new_value = strip_equals_signs(tokens[5]) if has_replacing?
|
18
|
+
end
|
19
|
+
|
20
|
+
def copybook_name
|
21
|
+
@copybook_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_period?
|
25
|
+
@has_period
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_replacing?
|
29
|
+
@has_replacing
|
30
|
+
end
|
31
|
+
|
32
|
+
def old_value
|
33
|
+
@old_value
|
34
|
+
end
|
35
|
+
|
36
|
+
def new_value
|
37
|
+
@new_value
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def strip_equals_signs value
|
43
|
+
value.gsub(/\=\=/, '')
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/lib/expander.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative '../lib/copy_expander'
|
2
|
+
require_relative '../lib/expander_config'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Command-line driver for COBOL copy expander utility.
|
6
|
+
#
|
7
|
+
class Expander
|
8
|
+
include CopyExpander
|
9
|
+
include ExpanderConfig
|
10
|
+
|
11
|
+
def initialize(argv=ARGV)
|
12
|
+
@argv = argv
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
load_config @argv
|
17
|
+
open_source_file
|
18
|
+
open_expanded_file
|
19
|
+
process_source
|
20
|
+
close_expanded_file
|
21
|
+
close_source_file
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_source
|
25
|
+
init
|
26
|
+
begin
|
27
|
+
output_line = process read_line
|
28
|
+
write_from output_line
|
29
|
+
end while @eof == false
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_line
|
33
|
+
line = @source_file.readline
|
34
|
+
@eof = @source_file.eof?
|
35
|
+
line
|
36
|
+
end
|
37
|
+
|
38
|
+
def write_from line
|
39
|
+
@expanded_file.write line
|
40
|
+
end
|
41
|
+
|
42
|
+
def eof?
|
43
|
+
@eof
|
44
|
+
end
|
45
|
+
|
46
|
+
def open_source_file
|
47
|
+
if @argv == nil || @argv[0] == nil
|
48
|
+
abort 'Usage: expander name-of-cobol-source-file'
|
49
|
+
end
|
50
|
+
@source_file = File.open("#{source_dir}/#{@argv[0]}", 'r')
|
51
|
+
@eof = false
|
52
|
+
end
|
53
|
+
|
54
|
+
def close_source_file
|
55
|
+
@source_file.close
|
56
|
+
end
|
57
|
+
|
58
|
+
def open_expanded_file
|
59
|
+
@expanded_file = File.open("#{output_dir('.')}/#{expanded_file('TEMP.CBL')}", 'w')
|
60
|
+
end
|
61
|
+
|
62
|
+
def close_expanded_file
|
63
|
+
@expanded_file.close
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require_relative '../lib/expander_config'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Load and manage configuration settings
|
6
|
+
#
|
7
|
+
module ExpanderConfig
|
8
|
+
|
9
|
+
def load_config argv
|
10
|
+
@config_file = argv.length > 1 ? "#{argv[1]}.yml" : 'expander.yml'
|
11
|
+
@config = File.exist?(@config_file) ? YAML.load_file(@config_file) : @config = {}
|
12
|
+
@config
|
13
|
+
end
|
14
|
+
|
15
|
+
def config_file
|
16
|
+
@config_file
|
17
|
+
end
|
18
|
+
|
19
|
+
def hash
|
20
|
+
@config
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(method_name, *args, &block)
|
24
|
+
@config.has_key?("#{method_name}") ? @config["#{method_name}"] : args[0]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/run-specs
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
"$HOME/.rvm/rubies/ruby-2.1.2/bin/rake"
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'copy_expander'
|
2
|
+
require 'copy_statement'
|
3
|
+
|
4
|
+
describe CopyExpander do
|
5
|
+
|
6
|
+
include CopyExpander
|
7
|
+
|
8
|
+
context 'preparing a source line to be processed' do
|
9
|
+
|
10
|
+
it 'recognizes a comment line' do
|
11
|
+
expect(comment?('123456*8901234567890')).to be true
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'recognizes a non-comment line' do
|
15
|
+
expect(comment?('12345678901234567890')).to be false
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'stores the first 6 characters of the line' do
|
19
|
+
break_up_source_line('12345678901234567890')
|
20
|
+
expect(@first_six_characters).to eq('123456')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'does not try to store the first 6 characters if the line is too short' do
|
24
|
+
break_up_source_line('12345')
|
25
|
+
expect(@first_six_characters).to be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'stores the last 8 characters of the line' do
|
29
|
+
break_up_source_line('1234567890'*7 + 'abcdefghij')
|
30
|
+
expect(@last_eight_characters).to eq('cdefghij')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'does not try to store the last 8 characters if the line is too short' do
|
34
|
+
break_up_source_line('1234567890'*7)
|
35
|
+
expect(@last_eight_characters).to be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'stores the intepreted portion of a source line' do
|
39
|
+
break_up_source_line('1234567890'*7 + 'abcdefghij')
|
40
|
+
expect(@work_area).to eq('7890' + '1234567890'*6 + 'ab')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does not try to store the intepreted portion of a source line that is too short' do
|
44
|
+
break_up_source_line('1234567890'*7)
|
45
|
+
expect(@work_area).to be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'identifying source lines that contain COPY statements' do
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
init
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'recognizes that the work area of a line contains a COPY statement' do
|
57
|
+
@work_area = ' something something COPY something'
|
58
|
+
expect(has_copy_statement?).to be true
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'recognizes that the work area of a line contains a copy statement' do
|
62
|
+
@work_area = ' something something copy something'
|
63
|
+
expect(has_copy_statement?).to be true
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'recognizes that the work area of a line does not contain a COPY statement' do
|
67
|
+
@work_area = ' something something something'
|
68
|
+
expect(has_copy_statement?).to be false
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'does not return a false positive when COPY appears as part of a variable name' do
|
72
|
+
@work_area = ' MOVE "N" TO WS-COPY-INDICATOR.'
|
73
|
+
expect(has_copy_statement?).to be false
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does not return a false positive when copy appears as part of a variable name' do
|
77
|
+
@work_area = ' set copy-allowed to true'
|
78
|
+
expect(has_copy_statement?).to be false
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'ignores the word COPY when it appears between single quotes' do
|
82
|
+
@work_area = ' DISPLAY \'I want to COPY something, and I want it now!\' '
|
83
|
+
expect(has_copy_statement?).to be false
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'ignores the word COPY when it appears between double quotes' do
|
87
|
+
@work_area = ' DISPLAY "I want to COPY something, and I want it now!" '
|
88
|
+
expect(has_copy_statement?).to be false
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'ignores the word COPY when it appears immediately after a single quote' do
|
92
|
+
@work_area = '\'COPY me this, Batman!\''
|
93
|
+
expect(has_copy_statement?).to be false
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'ignores the word COPY when it appears immediately before a single quote' do
|
97
|
+
@work_area = ' \'this is only a COPY\''
|
98
|
+
expect(has_copy_statement?).to be false
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'ignores the word COPY when it appears immediately after a double quote' do
|
102
|
+
@work_area = '"COPY me this, Batman!"'
|
103
|
+
expect(has_copy_statement?).to be false
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'ignores the word COPY when it appears immediately before a double quote' do
|
107
|
+
@work_area = ' "this is only a COPY"'
|
108
|
+
expect(has_copy_statement?).to be false
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'reconstructing and commentizing source lines' do
|
114
|
+
|
115
|
+
it 'reconstructs a source line' do
|
116
|
+
@work_area = ' something something'
|
117
|
+
@first_six_characters = '123456'
|
118
|
+
@last_eight_characters = 'abcdefgh'
|
119
|
+
expect(reconstruct_line).to eq('123456 something something' + ' '*40 + " abcdefgh\n")
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'reconstructs a source line as a comment line' do
|
123
|
+
@work_area = ' something something'
|
124
|
+
@first_six_characters = '123456'
|
125
|
+
@last_eight_characters = 'abcdefgh'
|
126
|
+
expect(commentize(reconstruct_line)).to eq('123456* something something' + ' '*40 + " abcdefgh\n")
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'substituting token values in a source line' do
|
132
|
+
|
133
|
+
it 'substitutes the new value for the old value in a source line' do
|
134
|
+
expect(replace_tokens(
|
135
|
+
' 05 :ALPHA:-FIELDNAME PIC X.',
|
136
|
+
CopyStatement.new(' COPY FOOBAR REPLACING ==:ALPHA:== BY ==BETA==.')))
|
137
|
+
.to eq(' 05 BETA-FIELDNAME PIC X.')
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Format a piece of COBOL source text as a fixed-length 80-character
|
144
|
+
# line with a six-digit line number in positions 1-6 and an arbitrary
|
145
|
+
# 8-character value in positions 73-80.
|
146
|
+
# The source text represents the contents of positions 7-72 of a line
|
147
|
+
# in a conventional COBOL source file.
|
148
|
+
#
|
149
|
+
def format_source_line line_number, text
|
150
|
+
first_six = '%6.6d' % line_number.to_s
|
151
|
+
('%-72.72s' % "#{first_six}#{text}") + "AAAAAAAA\n"
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'copy_statement'
|
2
|
+
|
3
|
+
describe CopyStatement do
|
4
|
+
|
5
|
+
context 'parsing out the copybook name' do
|
6
|
+
|
7
|
+
it 'handles a simple COPY statement with no period' do
|
8
|
+
copy_statement = CopyStatement.new ' COPY CPYBOOK1 '
|
9
|
+
expect(copy_statement.copybook_name).to eq('CPYBOOK1')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'handles a simple COPY statement with a period' do
|
13
|
+
copy_statement = CopyStatement.new ' COPY CPYBOOK1. '
|
14
|
+
expect(copy_statement.copybook_name).to eq('CPYBOOK1')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'handles a COPY REPLACING statement with a period' do
|
18
|
+
copy_statement = CopyStatement.new ' COPY CPYBOOK2 REPLACING ==:QUAL:== BY ==WS==.'
|
19
|
+
expect(copy_statement.copybook_name).to eq('CPYBOOK2')
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'remembering whether the COPY statement is terminated by a period' do
|
25
|
+
|
26
|
+
it 'remembers that a COPY statement is terminated by a period' do
|
27
|
+
copy_statement = CopyStatement.new ' COPY CPYBOOK1. '
|
28
|
+
expect(copy_statement.has_period?).to be_truthy
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'remembers that a COPY statement is not terminated by a period' do
|
32
|
+
copy_statement = CopyStatement.new ' COPY CPYBOOK1 '
|
33
|
+
expect(copy_statement.has_period?).to be_falsy
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'recognizing token replacement' do
|
39
|
+
|
40
|
+
it 'recognizes the REPLACING option' do
|
41
|
+
copy_statement = CopyStatement.new ' COPY CPYBOOK2 REPLACING ==:QUAL:== BY ==WS==.'
|
42
|
+
expect(copy_statement.has_replacing?).to be_truthy
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'recognizes when there is no REPLACING option' do
|
46
|
+
copy_statement = CopyStatement.new ' COPY CPYBOOK2.'
|
47
|
+
expect(copy_statement.has_replacing?).to be_falsy
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'stores the value to be replaced' do
|
51
|
+
copy_statement = CopyStatement.new ' COPY CPYBOOK2 REPLACING ==:QUAL:== BY ==WS==.'
|
52
|
+
expect(copy_statement.old_value).to eq(':QUAL:')
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'expander_config'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
describe ExpanderConfig do
|
5
|
+
|
6
|
+
include ExpanderConfig
|
7
|
+
|
8
|
+
context 'loading configuration data' do
|
9
|
+
|
10
|
+
it 'uses the default configuration file when none is passed' do
|
11
|
+
load_config []
|
12
|
+
expect(config_file).to eq('expander.yml')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'uses the specified configuration file when the name is passed' do
|
16
|
+
load_config [ 'x', 'configuration' ]
|
17
|
+
expect(config_file).to eq('configuration.yml')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns the value associated with a key if the key exists' do
|
21
|
+
load_config []
|
22
|
+
@config = { 'strange_name' => 'stored value' }
|
23
|
+
expect(strange_name('default value')).to eq('stored value')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns the default value when the key does not exist' do
|
27
|
+
load_config []
|
28
|
+
expect(strange_name('default value')).to eq('default value')
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Expander functional checks using real files
|
2
|
+
# These are not unit checks - there's no mocking.
|
3
|
+
|
4
|
+
require 'expander'
|
5
|
+
|
6
|
+
describe Expander, slow: true do
|
7
|
+
|
8
|
+
before(:all) do
|
9
|
+
File.open('expander.yml', 'w') do |file|
|
10
|
+
file.write("{\n")
|
11
|
+
file.write(" expanded_file: TEMP.CBL,\n")
|
12
|
+
file.write(" source_dir: cobol-source,\n")
|
13
|
+
file.write(" copy_dir: cobol-source/copy\n")
|
14
|
+
file.write("}\n")
|
15
|
+
file.close
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'copies a source file as-is when it contains no COPY statements' do
|
20
|
+
system('bin/expander NOCOPY.CBL')
|
21
|
+
expect_and_diff('./cobol-source/NOCOPY.CBL', './TEMP.CBL')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'expands a COPY REPLACING statement one level deep' do
|
25
|
+
system('bin/expander COPY1LVL.CBL')
|
26
|
+
expect_and_diff('./expected/EXPECTED1.CBL', './TEMP.CBL')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'expands two COPY REPLACING statements one level deep with different replacements' do
|
30
|
+
system('bin/expander COPY1LVLA.CBL')
|
31
|
+
expect_and_diff('./expected/EXPECTED2.CBL', './TEMP.CBL')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'expands one COPY REPLACING statement with a nested COPY REPLACING statement' do
|
35
|
+
system('bin/expander COPY2LVL.CBL')
|
36
|
+
expect_and_diff('./expected/EXPECTED3.CBL', './TEMP.CBL')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'expands COPY REPLACING statements down to three levels deep' do
|
40
|
+
system('bin/expander COPY3LVL.CBL')
|
41
|
+
expect_and_diff('./expected/EXPECTED4.CBL', './TEMP.CBL')
|
42
|
+
end
|
43
|
+
|
44
|
+
def expect_and_diff file1, file2
|
45
|
+
system("diff #{file1} #{file2}") unless FileUtils.compare_file(file1, file2)
|
46
|
+
expect(FileUtils.compare_file(file1, file2)).to be true
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'expander'
|
2
|
+
|
3
|
+
describe Expander, slow: true do
|
4
|
+
|
5
|
+
context 'opening and closing source files' do
|
6
|
+
|
7
|
+
it 'complains when no source file name is passed on the command line' do
|
8
|
+
stderr = $stderr
|
9
|
+
$stderr = StringIO.new
|
10
|
+
@expander = Expander.new nil
|
11
|
+
expect { @expander.open_source_file }.to raise_error(SystemExit)
|
12
|
+
$stderr.rewind
|
13
|
+
expect($stderr.readlines).to include("Usage: expander name-of-cobol-source-file\n")
|
14
|
+
$stderr = stderr
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'complains when it cannot find the file given on the command line' do
|
18
|
+
stderr = $stderr
|
19
|
+
$stderr = StringIO.new
|
20
|
+
@expander = Expander.new 'nosuchfile'
|
21
|
+
# expect { @expander.open_source_file }.to raise_error(Errno::ENOENT)
|
22
|
+
expect { @expander.open_source_file }.to raise_error(NoMethodError)
|
23
|
+
$stderr = stderr
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# snippets
|
2
|
+
|
3
|
+
|
4
|
+
##
|
5
|
+
# Format a piece of COBOL source text as a fixed-length 80-character
|
6
|
+
# line with a six-digit line number in positions 1-6 and an arbitrary
|
7
|
+
# 8-character value in positions 73-80.
|
8
|
+
# The source text represents the contents of positions 7-72 of a line
|
9
|
+
# in a conventional COBOL source file.
|
10
|
+
#
|
11
|
+
def format_source_line line_number, text
|
12
|
+
first_six = '%6.6d' % line_number.to_s
|
13
|
+
('%-72.72s' % "#{first_six}#{text}") + "AAAAAAAA\n"
|
14
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: copy-expander
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dave Nicolette
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Expands nested COBOL COPY REPLACING statements
|
56
|
+
email:
|
57
|
+
- davenicolette@gmail.com
|
58
|
+
executables:
|
59
|
+
- expander
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- Gemfile
|
65
|
+
- Gemspec
|
66
|
+
- LICENSE.txt
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- bin/expander
|
70
|
+
- cobol-source/COPY1LVL.CBL
|
71
|
+
- cobol-source/COPY1LVLA.CBL
|
72
|
+
- cobol-source/COPY2LVL.CBL
|
73
|
+
- cobol-source/COPY3LVL.CBL
|
74
|
+
- cobol-source/NOCOPY.CBL
|
75
|
+
- cobol-source/copy/COPY1
|
76
|
+
- cobol-source/copy/COPY1A
|
77
|
+
- cobol-source/copy/COPY2
|
78
|
+
- cobol-source/copy/COPY3
|
79
|
+
- copy-expander.gemspec
|
80
|
+
- expander.yml
|
81
|
+
- expected/EXPECTED1.CBL
|
82
|
+
- expected/EXPECTED2.CBL
|
83
|
+
- expected/EXPECTED3.CBL
|
84
|
+
- expected/EXPECTED4.CBL
|
85
|
+
- lib/copy-expander/version.rb
|
86
|
+
- lib/copy_expander.rb
|
87
|
+
- lib/copy_statement.rb
|
88
|
+
- lib/expander.rb
|
89
|
+
- lib/expander_config.rb
|
90
|
+
- run-specs
|
91
|
+
- spec/lib/copy-expander/copy_expander_spec.rb
|
92
|
+
- spec/lib/copy-expander/copy_statement_spec.rb
|
93
|
+
- spec/lib/copy-expander/expander_config_spec.rb
|
94
|
+
- spec/lib/copy-expander/expander_functional.rb
|
95
|
+
- spec/lib/copy-expander/expander_spec.rb
|
96
|
+
- spec/lib/copy-expander/snippets.txt
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
homepage: http://github.com/neopragma/copy-expander
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.2.2
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Expands nested COBOL COPY REPLACING statements
|
122
|
+
test_files:
|
123
|
+
- spec/lib/copy-expander/copy_expander_spec.rb
|
124
|
+
- spec/lib/copy-expander/copy_statement_spec.rb
|
125
|
+
- spec/lib/copy-expander/expander_config_spec.rb
|
126
|
+
- spec/lib/copy-expander/expander_functional.rb
|
127
|
+
- spec/lib/copy-expander/expander_spec.rb
|
128
|
+
- spec/lib/copy-expander/snippets.txt
|
129
|
+
- spec/spec_helper.rb
|