Skip to content

Commit 4f44404

Browse files
authored
Symbols - Document and support type mapping for basic PIC symbols (#11)
* Add some PIC and copybook docco * From doc matrix, prep fallthrough type filter * Add "complex" length calc * Update greedy regex * Update PIC type and length parsers * Use updated PIC type and length parsers * Update template and example * Update binary name and references to it * Update readme
1 parent 138f980 commit 4f44404

File tree

12 files changed

+253
-76
lines changed

12 files changed

+253
-76
lines changed

.golangci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ run:
1414
# from this option's value (see skip-dirs-use-default).
1515
skip-dirs:
1616
- dummyout
17+
- example
1718
# default is true. Enables skipping of directories:
1819
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
1920
skip-dirs-use-default: true

Makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ vendor: ## Cleans up go mod dependencies and vendor's all dependencies
2828
.PHONY: build
2929
build: clean
3030
go build -v \
31-
-o ./dist/go-pic \
31+
-o ./dist/gopic \
3232
cmd/main.go
3333

3434
install: build
35-
chmod +x ./dist/go-pic
36-
cp ./dist/go-pic $(GOPATH)/bin/go-pic
35+
chmod +x ./dist/gopic
36+
cp ./dist/gopic $(GOPATH)/bin/gopic
3737

3838
# Automated code review for Go
3939
.PHONY: tidy
@@ -56,8 +56,8 @@ cover: test ## Runs unit tests and assesses output coverage file
5656

5757
.PHONY: run
5858
run: build
59-
chmod +x ./dist/go-pic
60-
./dist/go-pic dir -i dummy -o dummyout
59+
chmod +x ./dist/gopic
60+
./dist/gopic dir -i dummy -o dummyout
6161

6262
define CHECK_COVERAGE
6363
awk \

README.md

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Get started using `gopic` by installing the struct-generation tool from the root
1515

1616
## Tag-based unmarshalling
1717

18-
go-pic's main focus is enabling simpler 1:1 mapping of PIC definitions to Go structs.
18+
`gopic`'s main focus is enabling simpler 1:1 mapping of PIC definitions to Go structs.
1919

2020
For example, if your PIC definitions look like:
2121
```
@@ -38,40 +38,41 @@ type Copybook struct{
3838
}
3939
```
4040

41-
Or you can make use of `go-pics` other feature below, so that you don't have to define the struct yourself...
41+
Or you can make use of `gopic`s other feature below, so that you don't have to define the struct yourself...
4242

4343
## Generating tagged structs
4444

45-
`go-pic` provides support to generate flattened Go struct representations of your COBOL copybooks, tagged with length statements and assigned the appropriate type for unmarshalling files that match your COBOL copybook definitions.
45+
`gopic` provides support to generate flattened Go struct representations of your COBOL copybooks, tagged with length statements and assigned the appropriate type for unmarshalling files that match your COBOL copybook definitions.
4646

4747
`cmd` contains go struct generation tool from textual PIC definitions
4848

4949
Example struct gen usage:
5050

5151
A file:
52-
`gopic file -o mycopybookstruct -i cobolstuff/copybook.txt`
52+
`$ gopic file -o mycopybookstruct -i cobolstuff/copybook.txt`
5353

5454
A directory of files:
55-
`gopic dir -o mystructsdir -i cobolstuff`
55+
`$ gopic dir -o mystructsdir -i cobolstuff`
5656

57-
When using `go-pic` for struct generation, additional, non-functional values are tagged to the PIC tags, for legibility's sake.
57+
When using `gopic` for struct generation, additional, non-functional values are tagged to the PIC tags, for legibility's sake.
5858

59-
For example, the example struct above, if generated with `go-pic` becomes:
59+
For example, the example struct above, if generated with `gopic` becomes:
6060

6161
```go
6262
type Copybook struct{
63-
Dummy1 int `pic:"4,1,4"`
64-
Dummy2 string `pic:"1,5,5"`
65-
Dummy3 int `pic:"4,6,9"`
66-
Dummy4 string `pic:"40,10,49"`
67-
Dummy5 string `pic:"40,50,89"`
63+
Dummy1 int `pic:"4"` // start:1 end:4
64+
Dummy2 string `pic:"1"` // start:5 end:5
65+
Dummy3 int `pic:"4"` // start:6 end:9
66+
Dummy4 string `pic:"40"` // start:10 end:49
67+
Dummy5 string `pic:"40"` // start:50 end:89
6868
}
6969
```
7070

71-
where the values represent:
71+
where the values...
7272
```go
73-
Dummy1 int `pic:"4"` // start:1, end:4
73+
Dummy1 int `pic:"4"` // start:1 end:4
7474
```
75-
- 4, pos 1 = length
76-
- 1, pos 2 = starting index
77-
- 4, pos 3 = ending index
75+
Represent:
76+
- 4 = length
77+
- 1 = start index
78+
- 4 = ending index

cmd/pkg/decoder/README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# PIC clauses in Copybooks
2+
3+
## What does a line in a copybook look like?
4+
Basic info below sourced from [tutorialspoint](https://www.tutorialspoint.com/cobol/cobol_data_types.htm).
5+
```
6+
01 TOTAL-STUDENTS PIC9(5) VALUE '125'.
7+
| | | |
8+
| | | |
9+
| | | |
10+
Level Number Data Name Picture Clause Value Clause
11+
```
12+
13+
### Level Number
14+
Level number is used to specify the level of data in a record. They are used for differentiating between elementary items and group items. Elementary items can be grouped together to create group items.
15+
16+
Level numbers adhere to the following rules:
17+
18+
| Sr.No. | Level Number & Description
19+
|----------|----------------------------
20+
| 01 | Record description entry
21+
| 02 - 49 | Group and Elementary items
22+
| 66 | Rename Clause items
23+
| 77 | Items which cannot be sub-divided
24+
| 88 | Condition name entry
25+
26+
- Elementary items cannot be divided further. Level number, Data name, Picture clause, and Value clause (optional) are used to describe an elementary item.
27+
28+
- Group items consist of one or more elementary items. Level number, Data name, and Value clause (optional) are used to describe a group item. Group level number is always 01
29+
30+
### Data Name
31+
Data names must be defined in the Data Division before using them in the Procedure Division. They must have a user-defined name; reserved words cannot be used. Data names give reference to the memory locations where actual data is stored. They can be elementary or group type.
32+
33+
### Picture clause
34+
35+
A Picture clause is used to define the following items:
36+
37+
- Data type can be numeric, alphabetic, or alphanumeric. Numeric type consists of only digits 0 to 9. Alphabetic type consists of letters A to Z and spaces. Alphanumeric type consists of digits, letters, and special characters.
38+
39+
- Sign can be used with numeric data. It can be either + or –.
40+
41+
- Decimal point position can be used with numeric data. Assumed position is the position of decimal point and not included in the data.
42+
43+
- Length defines the number of bytes used by the data item.
44+
45+
| Symbol | Description
46+
|--------|----------------------------
47+
| 9 | Numeric
48+
| A | Alphabetic
49+
| X | Alphanumeric
50+
| V | Implicit decimal
51+
| S | Sign
52+
| P | Assumed decimal place
53+
54+
## Possible PIC definitions
55+
Basic:
56+
- `PIC X.`
57+
- `PIC 9999.`
58+
- `PIC X(4).`
59+
60+
Mixed:
61+
- `PIC S99V99`
62+
- `PIC S9(3)V9(2).`
63+
- `PIC PPP999.`
64+
65+
### Determining applicable Go Types
66+
`i` - Int
67+
`u` - Uint
68+
`s` - String
69+
70+
| *X* | *9* | *A* | *X* | *V* | *S* | *P*
71+
|-----|-----|-----|-----|-----|-----|-----
72+
| *9* | u | s | s | u | i | u
73+
| *A* | s | s | s | s | s | s
74+
| *X* | s | s | s | s | s | s
75+
| *V* | u | s | s | ? | i | u
76+
| *S* | i | s | s | i | i | i
77+
| *P* | u | s | s | u | i | u
78+
79+
### Applying regex
80+
PICs can essentially be comprised of any pattern of the above symbols
81+
82+
`S9(5)VXXXXA(12)` is a valid definition for `-12345.AB12DABCDEFGHIJKL`
83+
84+
This quickly becomes difficult to handle with regex. So support for basic patterns will be created
85+
86+
Signed Numbers:
87+
- `PIC S9(3)V9(2).`
88+
89+
Decimals:
90+
- `PIC PPP999.`
91+
92+

cmd/pkg/decoder/decode.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"log"
10+
"reflect"
1011
"strconv"
1112
"strings"
1213
"sync"
@@ -165,9 +166,9 @@ func (d *decoder) picRecord(line string) (*copybook.Record, error) {
165166
return nil, err
166167
}
167168

168-
pic, err := parsePICType(ss[4])
169-
if err != nil {
170-
return nil, err
169+
pic := parsePICType(ss[4])
170+
if pic == reflect.Invalid {
171+
return nil, errors.New("picTypeFilter: unexpected PIC type")
171172
}
172173

173174
size, err := parsePICCount(ss[4])
@@ -200,9 +201,9 @@ func (d *decoder) incompletePICRecord(line string) (*copybook.Record, error) {
200201
return nil, errors.New("incompletePICRecord: does not match expected length/format")
201202
}
202203

203-
pic, err := parsePICType(ss[3])
204-
if err != nil {
205-
return nil, err
204+
pic := parsePICType(ss[3])
205+
if pic == reflect.Invalid {
206+
return nil, errors.New("picTypeFilter: unexpected PIC type")
206207
}
207208

208209
size, err := parsePICCount(ss[3])
@@ -320,11 +321,10 @@ func (d *decoder) occursRecord(line string) (*copybook.Record, error) {
320321
return nil, err
321322
}
322323

323-
t, err := parsePICType(ss[4])
324-
if err != nil {
325-
return nil, err
324+
pic := parsePICType(ss[4])
325+
if pic == reflect.Invalid {
326+
return nil, errors.New("picTypeFilter: unexpected PIC type")
326327
}
327-
328328
size, err := parsePICCount(ss[4])
329329
if err != nil {
330330
return nil, err
@@ -339,7 +339,7 @@ func (d *decoder) occursRecord(line string) (*copybook.Record, error) {
339339
Num: num,
340340
Level: lvl,
341341
Name: ss[2],
342-
Picture: t,
342+
Picture: pic,
343343
Length: size,
344344
Occurs: occurs,
345345
}
@@ -387,9 +387,9 @@ func (d *decoder) multiLineOccursRecord(line string) (*copybook.Record, error) {
387387
return nil, err
388388
}
389389

390-
t, err := parsePICType(ss[4])
391-
if err != nil {
392-
return nil, err
390+
pic := parsePICType(ss[4])
391+
if pic == reflect.Invalid {
392+
return nil, errors.New("picTypeFilter: unexpected PIC type")
393393
}
394394

395395
size, err := parsePICCount(ss[4])
@@ -406,7 +406,7 @@ func (d *decoder) multiLineOccursRecord(line string) (*copybook.Record, error) {
406406
Num: num,
407407
Level: lvl,
408408
Name: ss[2],
409-
Picture: t,
409+
Picture: pic,
410410
Length: size,
411411
Occurs: occurs,
412412
}

cmd/pkg/decoder/decode_test.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ package tempcopybook
4141
type Copybookdummy struct {
4242
DUMMY1 string ` + "`" + `pic:"1"` + "`" + ` // start:1 end:1
4343
DUMMY2 string ` + "`" + `pic:"3"` + "`" + ` // start:2 end:4
44-
DUMMY3 int ` + "`" + `pic:"7"` + "`" + ` // start:5 end:11
45-
DUMMY4 int ` + "`" + `pic:"4"` + "`" + ` // start:12 end:15
44+
DUMMY3 uint ` + "`" + `pic:"7"` + "`" + ` // start:5 end:11
45+
DUMMY4 uint ` + "`" + `pic:"4"` + "`" + ` // start:12 end:15
4646
DUMMY5 string ` + "`" + `pic:"2"` + "`" + ` // start:16 end:17
47-
DUMMY6 int ` + "`" + `pic:"7"` + "`" + ` // start:18 end:24
47+
DUMMY6 uint ` + "`" + `pic:"7"` + "`" + ` // start:18 end:24
4848
DUMMY7 string ` + "`" + `pic:"10"` + "`" + ` // start:25 end:34
4949
DUMMY8 string ` + "`" + `pic:"1"` + "`" + ` // start:35 end:35
5050
}
@@ -95,14 +95,14 @@ func Test_decoder_findDataRecord(t *testing.T) {
9595
Length: 3,
9696
},
9797
}, {
98-
name: "BasicPICIntParentheses",
98+
name: "BasicPICUintParentheses",
9999
line: "000620 10 DUMMY-3 PIC 9(7). 00000169",
100100
c: copybook.New("dummy", template.CopyBook),
101101
want: &copybook.Record{
102102
Num: 620,
103103
Level: 10,
104104
Name: "DUMMY-3",
105-
Picture: reflect.Int,
105+
Picture: reflect.Uint,
106106
Length: 7,
107107
},
108108
}, {
@@ -116,6 +116,28 @@ func Test_decoder_findDataRecord(t *testing.T) {
116116
Picture: reflect.String,
117117
Length: 2,
118118
},
119+
}, {
120+
name: "MultiTypeMultiParenthesesNumber",
121+
line: "000640 10 DUMMY-5 PIC S9(9)V9(9). 00000171",
122+
c: copybook.New("dummy", template.CopyBook),
123+
want: &copybook.Record{
124+
Num: 640,
125+
Level: 10,
126+
Name: "DUMMY-5",
127+
Picture: reflect.Int,
128+
Length: 20,
129+
},
130+
}, {
131+
name: "MultiTypeMultiParenthesesString",
132+
line: "000640 10 DUMMY-5 PIC A(9)V9(9). 00000171",
133+
c: copybook.New("dummy", template.CopyBook),
134+
want: &copybook.Record{
135+
Num: 640,
136+
Level: 10,
137+
Name: "DUMMY-5",
138+
Picture: reflect.String,
139+
Length: 19,
140+
},
119141
},
120142
}
121143
for _, test := range tests {

cmd/pkg/decoder/lines.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ var (
3636
// Defines picture clause
3737
// 000600 10 DUMMY-1 PIC X. 00000167
3838
// 000620 10 DUMMY-2 PIC 9(7). 00000169
39-
picLine = regexp.MustCompile(`^[0-9]+ +[0-9]{2} +[a-zA-Z0-9\-]+ +PIC ([X9]+|[X9]\([0-9]+\))\. +0+[0-9]+$`)
39+
generousPICLine = regexp.MustCompile(`^[0-9]+ +[0-9]{2} +[a-zA-Z0-9\-]+ +PIC [AXPVS()0-9]+\. +0+[0-9]+$`)
4040

4141
// Defines picture clause that deviates from typical pattern
4242
// 10 DUMMY-1 PIC X. 00000167
4343
// 10 DUMMY-2 PIC 9(7). 00000169
44-
incompletePICLine = regexp.MustCompile(`[0-9]{2} +[a-zA-Z0-9\-]+ +PIC ([X9]+|[X9]\([0-9]+\))\.`)
44+
generousIncompletePICLine = regexp.MustCompile(`[0-9]{2} +[a-zA-Z0-9\-]+ +PIC [AXPVS()0-9]+\.`)
4545

4646
// Matches same-line REDEFINES definitions
4747
// 000550 05 DUMMY-1 PIC X(340). 00000162
@@ -75,11 +75,11 @@ func getLineType(line string) lineKind {
7575
return redefinesSingle
7676
}
7777

78-
if picLine.MatchString(line) {
78+
if generousPICLine.MatchString(line) {
7979
return pic
8080
}
8181

82-
if incompletePICLine.MatchString(line) {
82+
if generousIncompletePICLine.MatchString(line) {
8383
return picIncomplete
8484
}
8585

0 commit comments

Comments
 (0)