@jdeighan/coffee-utils 8.0.4 → 8.0.7

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jdeighan/coffee-utils",
3
3
  "type": "module",
4
- "version": "8.0.4",
4
+ "version": "8.0.7",
5
5
  "description": "A set of utility functions for CoffeeScript",
6
6
  "main": "coffee_utils.js",
7
7
  "exports": {
@@ -10,36 +10,36 @@ import {indented} from '@jdeighan/coffee-utils/indent'
10
10
 
11
11
  export class Section
12
12
 
13
- constructor: () ->
13
+ constructor: (@name) ->
14
14
 
15
- @lLines = []
15
+ @lParts = []
16
16
 
17
17
  # ..........................................................
18
18
 
19
19
  length: () ->
20
20
 
21
- return @lLines.length
21
+ return @lParts.length
22
22
 
23
23
  # ..........................................................
24
24
 
25
25
  indent: (level=1) ->
26
26
 
27
- lNewLines = for line in @lLines
27
+ lNewLines = for line in @lParts
28
28
  indented(line, level)
29
- @lLines = lNewLines
29
+ @lParts = lNewLines
30
30
  return
31
31
 
32
32
  # ..........................................................
33
33
 
34
34
  isEmpty: () ->
35
35
 
36
- return (@lLines.length == 0)
36
+ return (@lParts.length == 0)
37
37
 
38
38
  # ..........................................................
39
39
 
40
40
  nonEmpty: () ->
41
41
 
42
- return (@lLines.length > 0)
42
+ return (@lParts.length > 0)
43
43
 
44
44
  # ..........................................................
45
45
 
@@ -47,9 +47,9 @@ export class Section
47
47
 
48
48
  if isArray(data)
49
49
  for line in data
50
- @lLines.push line
50
+ @lParts.push line
51
51
  else
52
- @lLines.push data
52
+ @lParts.push data
53
53
  return
54
54
 
55
55
  # ..........................................................
@@ -57,13 +57,22 @@ export class Section
57
57
  prepend: (data) ->
58
58
 
59
59
  if isArray(data)
60
- @lLines = [data..., @lLines...]
60
+ @lParts = [data..., @lParts...]
61
61
  else
62
- @lLines = [data, @lLines...]
62
+ @lParts = [data, @lParts...]
63
63
  return
64
64
 
65
65
  # ..........................................................
66
66
 
67
+ getParts: () ->
68
+
69
+ return @lParts
70
+
71
+ # ..........................................................
72
+
67
73
  getBlock: () ->
68
74
 
69
- return arrayToBlock(@lLines)
75
+ if (@lParts.length == 0)
76
+ return undef
77
+ else
78
+ return arrayToBlock(@lParts)
package/src/Section.js CHANGED
@@ -19,13 +19,14 @@ import {
19
19
 
20
20
  // ---------------------------------------------------------------------------
21
21
  export var Section = class Section {
22
- constructor() {
23
- this.lLines = [];
22
+ constructor(name) {
23
+ this.name = name;
24
+ this.lParts = [];
24
25
  }
25
26
 
26
27
  // ..........................................................
27
28
  length() {
28
- return this.lLines.length;
29
+ return this.lParts.length;
29
30
  }
30
31
 
31
32
  // ..........................................................
@@ -33,7 +34,7 @@ export var Section = class Section {
33
34
  var lNewLines, line;
34
35
  lNewLines = (function() {
35
36
  var i, len, ref, results;
36
- ref = this.lLines;
37
+ ref = this.lParts;
37
38
  results = [];
38
39
  for (i = 0, len = ref.length; i < len; i++) {
39
40
  line = ref[i];
@@ -41,17 +42,17 @@ export var Section = class Section {
41
42
  }
42
43
  return results;
43
44
  }).call(this);
44
- this.lLines = lNewLines;
45
+ this.lParts = lNewLines;
45
46
  }
46
47
 
47
48
  // ..........................................................
48
49
  isEmpty() {
49
- return this.lLines.length === 0;
50
+ return this.lParts.length === 0;
50
51
  }
51
52
 
52
53
  // ..........................................................
53
54
  nonEmpty() {
54
- return this.lLines.length > 0;
55
+ return this.lParts.length > 0;
55
56
  }
56
57
 
57
58
  // ..........................................................
@@ -60,25 +61,34 @@ export var Section = class Section {
60
61
  if (isArray(data)) {
61
62
  for (i = 0, len = data.length; i < len; i++) {
62
63
  line = data[i];
63
- this.lLines.push(line);
64
+ this.lParts.push(line);
64
65
  }
65
66
  } else {
66
- this.lLines.push(data);
67
+ this.lParts.push(data);
67
68
  }
68
69
  }
69
70
 
70
71
  // ..........................................................
71
72
  prepend(data) {
72
73
  if (isArray(data)) {
73
- this.lLines = [...data, ...this.lLines];
74
+ this.lParts = [...data, ...this.lParts];
74
75
  } else {
75
- this.lLines = [data, ...this.lLines];
76
+ this.lParts = [data, ...this.lParts];
76
77
  }
77
78
  }
78
79
 
80
+ // ..........................................................
81
+ getParts() {
82
+ return this.lParts;
83
+ }
84
+
79
85
  // ..........................................................
80
86
  getBlock() {
81
- return arrayToBlock(this.lLines);
87
+ if (this.lParts.length === 0) {
88
+ return undef;
89
+ } else {
90
+ return arrayToBlock(this.lParts);
91
+ }
82
92
  }
83
93
 
84
94
  };
@@ -3,8 +3,10 @@
3
3
  import {
4
4
  assert, pass, undef, defined, croak, OL, isEmpty, nonEmpty,
5
5
  isString, isHash, isArray, isUniqueTree, isNonEmptyString,
6
+ isNonEmptyArray,
6
7
  } from '@jdeighan/coffee-utils'
7
8
  import {arrayToBlock} from '@jdeighan/coffee-utils/block'
9
+ import {indented} from '@jdeighan/coffee-utils/indent'
8
10
  import {debug} from '@jdeighan/coffee-utils/debug'
9
11
  import {Section} from '@jdeighan/coffee-utils/section'
10
12
 
@@ -12,110 +14,148 @@ import {Section} from '@jdeighan/coffee-utils/section'
12
14
 
13
15
  isSectionName = (name) ->
14
16
 
15
- assert isString(name), "not a string"
16
- return name.match(/^[a-z][a-z0-9]*/)
17
+ return isString(name) && name.match(/^[a-z][a-z0-9]*/)
17
18
 
18
19
  # ---------------------------------------------------------------------------
19
20
 
20
21
  isSetName = (name) ->
21
22
 
22
- assert isString(name), "not a string"
23
- return name.match(/^[A-Z][a-z0-9]*/)
23
+ return isString(name) && name.match(/^[A-Z][a-z0-9]*/)
24
24
 
25
25
  # ---------------------------------------------------------------------------
26
26
 
27
27
  export class SectionMap
28
28
 
29
- constructor: (lSections) ->
30
- # --- lSections is a tree of section names
29
+ constructor: (lSectionTree) ->
30
+ # --- lSectionTree is a tree of section names
31
31
 
32
- debug "enter SectionMap()", lSections
33
- @lSections = lSections # a tree of section names
32
+ debug "enter SectionMap()", lSectionTree
33
+ @lSectionTree = lSectionTree # a tree of section names
34
34
  @hSets = {}
35
35
  @hSections = {}
36
- @addSections lSections
36
+ @addSections lSectionTree
37
37
  debug "return from SectionMap()", @hSections
38
38
 
39
39
  # ..........................................................
40
40
 
41
- # --- TODO: Allow array to start with a set name ---
42
-
43
41
  addSections: (desc) ->
42
+ # --- returns a flat array of sections that were added
44
43
 
45
44
  if isString(desc)
46
45
  assert nonEmpty(desc), "empty section name"
47
- assert desc.match(/^[a-z][a-z0-9]*$/), "bad section name #{OL(desc)}"
46
+ assert isSectionName(desc), "bad section name #{OL(desc)}"
48
47
  assert (@hSections[desc] == undef), "duplicate section #{OL(desc)}"
49
- @hSections[desc] = new Section()
48
+ @hSections[desc] = new Section(desc)
49
+ return [desc]
50
50
  else
51
51
  assert isArray(desc), "not an array or string #{OL(desc)}"
52
- for item in desc
53
- @addSections item
54
- return
55
-
56
- # ..........................................................
57
- # --- a generator - yield order is not guaranteed
58
-
59
- allSections: (desc) ->
60
-
61
- debug "enter allSections()"
62
- if (desc == undef)
63
- # --- We want to return all sections
64
- debug "yield all sections"
65
- for _,sect of @hSections
66
- yield sect
67
- else if isString(desc)
68
- if isSectionName(desc)
69
- debug "yield section #{OL(desc)}"
70
- yield @section(desc)
71
- else if isSetName(desc)
72
- debug "expand set #{OL(desc)}"
73
- debug 'hSets', @hSets
74
- lNames = @hSets[desc]
75
- assert defined(lNames), "no set named #{OL(desc)}"
76
- debug "set #{desc}", lNames
77
- for name in lNames
78
- debug "yield section #{OL(desc)}"
79
- yield @section(name)
80
- else
81
- croak "bad name #{OL(desc)}"
82
- else
83
- assert isArray(desc), "not an array"
84
- for name in desc
85
- debug "yield section #{OL(name)}"
86
- assert isString(name), "not a string #{OL(name)}"
87
- if isSectionName(name)
88
- yield @section(name)
89
- else if isSetName(name)
90
- for _,sect of @hSets[name]
91
- yield sect
52
+ name = undef
53
+ lParts = []
54
+ for item,i in desc
55
+ if (i==0) && isSetName(item)
56
+ name = item
92
57
  else
93
- croak "bad name #{OL(name)}"
94
- debug "return from allSections()"
58
+ lAdded = @addSections item
59
+ for item in lAdded
60
+ lParts.push item
61
+ if defined(name)
62
+ @addSet name, lParts
63
+ return lParts
95
64
  return
96
65
 
97
66
  # ..........................................................
98
67
 
99
- addSet: (name, lSections) ->
68
+ addSet: (name, lSectionTree) ->
100
69
 
101
- debug "enter addSet()", name, lSections
70
+ debug "enter addSet()", name, lSectionTree
102
71
 
103
72
  # --- check the name
104
73
  assert isSetName(name), "not a valid set name #{OL(name)}"
105
74
 
106
- # --- check lSections
107
- assert isArray(lSections), "arg 2 not an array"
108
- for secName in lSections
75
+ # --- check lSectionTree
76
+ assert isArray(lSectionTree), "arg 2 not an array"
77
+ for secName in lSectionTree
109
78
  assert isNonEmptyString(secName),
110
79
  "not a non-empty string #{OL(secName)}"
111
80
  assert defined(@hSections[secName]),
112
81
  "not a section name #{OL(secName)}"
113
82
 
114
- @hSets[name] = lSections
83
+ @hSets[name] = lSectionTree
115
84
  debug 'hSets', @hSets
116
85
  debug "return from addSet()"
117
86
  return
118
87
 
88
+ # ..........................................................
89
+ # --- sections returned in depth-first order from section tree
90
+ # Set names are simply skipped
91
+ # yields: [<level>, <section>]
92
+
93
+ allSections: (desc=undef, level=0) ->
94
+
95
+ debug "enter allSections()", desc, level
96
+ if (desc == undef)
97
+ desc = @lSectionTree
98
+ if isArray(desc)
99
+ for item in desc
100
+ if isSectionName(item)
101
+ result = [level, @section(item)]
102
+ debug 'yield', result
103
+ yield result
104
+ else if isSetName(item)
105
+ pass
106
+ else
107
+ assert isArray(item), "not an array #{OL(item)}"
108
+ yield from @allSections(item, level+1)
109
+ else if isSectionName(desc)
110
+ result = [level, @section(desc)]
111
+ debug 'yield', result
112
+ yield result
113
+ else if isSetName(desc)
114
+ lTree = @hSets[desc]
115
+ assert defined(lTree), "Not a Set: #{OL(desc)}"
116
+ yield from @allSections(lTree, level)
117
+ else
118
+ croak "Bad item: #{OL(desc)}"
119
+ debug "return from allSections()"
120
+ return
121
+
122
+ # ..........................................................
123
+ # --- procFunc should be (name, block) ->
124
+ # return processed block
125
+
126
+ getBlock: (procFunc=undef, lTree=undef) ->
127
+
128
+ debug "enter getBlock()"
129
+ if (lTree == undef)
130
+ lTree = @lSectionTree
131
+ else
132
+ assert isArray(lTree), "not an array #{OL(lTree)}"
133
+
134
+ lParts = []
135
+ for part in lTree
136
+ if isString(part)
137
+ text = @section(part).getBlock()
138
+ if nonEmpty(text) && defined(procFunc)
139
+ text = procFunc(part, text)
140
+ else if isNonEmptyArray(part)
141
+ if isSectionName(part[0])
142
+ text = @getBlock(procFunc, part)
143
+ else if isSetName(part[0])
144
+ text = @getBlock(procFunc, part.slice(1))
145
+ if nonEmpty(text) && defined(procFunc)
146
+ text = procFunc(part[0], text)
147
+ else
148
+ croak "Bad part: #{OL(part)}"
149
+ else
150
+ croak "Bad part: #{OL(part)}"
151
+ if defined(text)
152
+ lParts.push text
153
+
154
+ debug 'lParts', lParts
155
+ result = arrayToBlock(lParts)
156
+ debug "return from getBlock()", result
157
+ return result
158
+
119
159
  # ..........................................................
120
160
 
121
161
  section: (name) ->
@@ -129,27 +169,27 @@ export class SectionMap
129
169
  firstSection: (name) ->
130
170
 
131
171
  assert isSetName(name), "bad set name #{OL(name)}"
132
- lSections = @hSets[name]
133
- assert defined(lSections), "no such set #{OL(name)}"
134
- assert nonEmpty(lSections), "empty section #{OL(name)}"
135
- return @section(lSections[0])
172
+ lSectionTree = @hSets[name]
173
+ assert defined(lSectionTree), "no such set #{OL(name)}"
174
+ assert nonEmpty(lSectionTree), "empty section #{OL(name)}"
175
+ return @section(lSectionTree[0])
136
176
 
137
177
  # ..........................................................
138
178
 
139
179
  lastSection: (name) ->
140
180
 
141
181
  assert isSetName(name), "bad set name #{OL(name)}"
142
- lSections = @hSets[name]
143
- assert defined(lSections), "no such set #{OL(name)}"
144
- assert nonEmpty(lSections), "empty section #{OL(name)}"
145
- return @section(lSections[lSections.length - 1])
182
+ lSectionTree = @hSets[name]
183
+ assert defined(lSectionTree), "no such set #{OL(name)}"
184
+ assert nonEmpty(lSectionTree), "empty section #{OL(name)}"
185
+ return @section(lSectionTree[lSectionTree.length - 1])
146
186
 
147
187
  # ..........................................................
148
188
 
149
189
  length: (desc=undef) ->
150
190
 
151
191
  result = 0
152
- for sect from @allSections(desc)
192
+ for [_, sect] from @allSections(desc)
153
193
  result += sect.length()
154
194
  return result
155
195
 
@@ -167,55 +207,12 @@ export class SectionMap
167
207
 
168
208
  # ..........................................................
169
209
 
170
- indent: (desc=undef, level=1) ->
210
+ getShape: () ->
171
211
 
172
- for sect from @allSections(desc)
173
- sect.indent(level)
174
- return
175
-
176
- # ..........................................................
177
-
178
- enclose: (name, pre, post) ->
179
-
180
- if isSectionName(name)
181
- sect = @section(name)
182
- if sect.nonEmpty()
183
- sect.indent()
184
- sect.prepend(pre)
185
- sect.add(post)
186
- else if isSetName(name)
187
- if @nonEmpty(name)
188
- @indent(name)
189
- @firstSection(name).prepend(pre)
190
- @lastSection(name).add(post)
191
- else
192
- croak "Bad name param #{OL(name)}"
193
- return
194
-
195
- # ..........................................................
196
-
197
- getBlock: () ->
198
-
199
- debug "enter getBlock()"
200
- @lAllBlocks = []
201
- debug 'lSections', @lSections
202
- @accumBlock @lSections
203
- debug 'lAllBlocks', @lAllBlocks
204
- result = arrayToBlock(@lAllBlocks)
205
- debug "return from getBlock()", result
212
+ debug "enter getShape()"
213
+ lParts = []
214
+ for [level, sect] from @allSections()
215
+ lParts.push indented(sect.name, level)
216
+ result = arrayToBlock(lParts)
217
+ debug "return from getShape()", result
206
218
  return result
207
-
208
- # ..........................................................
209
-
210
- accumBlock: (tree) ->
211
-
212
- if isString(tree)
213
- debug "accumBlock #{OL(tree)}"
214
- block = @section(tree).getBlock()
215
- if nonEmpty(block)
216
- @lAllBlocks.push block
217
- else
218
- assert isArray(tree), "not an array"
219
- for subtree in tree
220
- @accumBlock subtree
221
- return
package/src/SectionMap.js CHANGED
@@ -15,13 +15,18 @@ import {
15
15
  isHash,
16
16
  isArray,
17
17
  isUniqueTree,
18
- isNonEmptyString
18
+ isNonEmptyString,
19
+ isNonEmptyArray
19
20
  } from '@jdeighan/coffee-utils';
20
21
 
21
22
  import {
22
23
  arrayToBlock
23
24
  } from '@jdeighan/coffee-utils/block';
24
25
 
26
+ import {
27
+ indented
28
+ } from '@jdeighan/coffee-utils/indent';
29
+
25
30
  import {
26
31
  debug
27
32
  } from '@jdeighan/coffee-utils/debug';
@@ -32,116 +37,156 @@ import {
32
37
 
33
38
  // ---------------------------------------------------------------------------
34
39
  isSectionName = function(name) {
35
- assert(isString(name), "not a string");
36
- return name.match(/^[a-z][a-z0-9]*/);
40
+ return isString(name) && name.match(/^[a-z][a-z0-9]*/);
37
41
  };
38
42
 
39
43
  // ---------------------------------------------------------------------------
40
44
  isSetName = function(name) {
41
- assert(isString(name), "not a string");
42
- return name.match(/^[A-Z][a-z0-9]*/);
45
+ return isString(name) && name.match(/^[A-Z][a-z0-9]*/);
43
46
  };
44
47
 
45
48
  // ---------------------------------------------------------------------------
46
49
  export var SectionMap = class SectionMap {
47
- constructor(lSections) {
48
- // --- lSections is a tree of section names
49
- debug("enter SectionMap()", lSections);
50
- this.lSections = lSections; // a tree of section names
50
+ constructor(lSectionTree) {
51
+ // --- lSectionTree is a tree of section names
52
+ debug("enter SectionMap()", lSectionTree);
53
+ this.lSectionTree = lSectionTree; // a tree of section names
51
54
  this.hSets = {};
52
55
  this.hSections = {};
53
- this.addSections(lSections);
56
+ this.addSections(lSectionTree);
54
57
  debug("return from SectionMap()", this.hSections);
55
58
  }
56
59
 
57
60
  // ..........................................................
58
-
59
- // --- TODO: Allow array to start with a set name ---
60
61
  addSections(desc) {
61
- var i, item, len;
62
+ var i, item, j, k, lAdded, lParts, len, len1, name;
63
+ // --- returns a flat array of sections that were added
62
64
  if (isString(desc)) {
63
65
  assert(nonEmpty(desc), "empty section name");
64
- assert(desc.match(/^[a-z][a-z0-9]*$/), `bad section name ${OL(desc)}`);
66
+ assert(isSectionName(desc), `bad section name ${OL(desc)}`);
65
67
  assert(this.hSections[desc] === undef, `duplicate section ${OL(desc)}`);
66
- this.hSections[desc] = new Section();
68
+ this.hSections[desc] = new Section(desc);
69
+ return [desc];
67
70
  } else {
68
71
  assert(isArray(desc), `not an array or string ${OL(desc)}`);
69
- for (i = 0, len = desc.length; i < len; i++) {
72
+ name = undef;
73
+ lParts = [];
74
+ for (i = j = 0, len = desc.length; j < len; i = ++j) {
70
75
  item = desc[i];
71
- this.addSections(item);
76
+ if ((i === 0) && isSetName(item)) {
77
+ name = item;
78
+ } else {
79
+ lAdded = this.addSections(item);
80
+ for (k = 0, len1 = lAdded.length; k < len1; k++) {
81
+ item = lAdded[k];
82
+ lParts.push(item);
83
+ }
84
+ }
72
85
  }
86
+ if (defined(name)) {
87
+ this.addSet(name, lParts);
88
+ }
89
+ return lParts;
90
+ }
91
+ }
92
+
93
+ // ..........................................................
94
+ addSet(name, lSectionTree) {
95
+ var j, len, secName;
96
+ debug("enter addSet()", name, lSectionTree);
97
+ // --- check the name
98
+ assert(isSetName(name), `not a valid set name ${OL(name)}`);
99
+ // --- check lSectionTree
100
+ assert(isArray(lSectionTree), "arg 2 not an array");
101
+ for (j = 0, len = lSectionTree.length; j < len; j++) {
102
+ secName = lSectionTree[j];
103
+ assert(isNonEmptyString(secName), `not a non-empty string ${OL(secName)}`);
104
+ assert(defined(this.hSections[secName]), `not a section name ${OL(secName)}`);
73
105
  }
106
+ this.hSets[name] = lSectionTree;
107
+ debug('hSets', this.hSets);
108
+ debug("return from addSet()");
74
109
  }
75
110
 
76
111
  // ..........................................................
77
- // --- a generator - yield order is not guaranteed
78
- * allSections(desc) {
79
- var _, i, j, lNames, len, len1, name, ref, ref1, sect;
80
- debug("enter allSections()");
112
+ // --- sections returned in depth-first order from section tree
113
+ // Set names are simply skipped
114
+ // yields: [<level>, <section>]
115
+ * allSections(desc = undef, level = 0) {
116
+ var item, j, lTree, len, result;
117
+ debug("enter allSections()", desc, level);
81
118
  if (desc === undef) {
82
- // --- We want to return all sections
83
- debug("yield all sections");
84
- ref = this.hSections;
85
- for (_ in ref) {
86
- sect = ref[_];
87
- yield sect;
88
- }
89
- } else if (isString(desc)) {
90
- if (isSectionName(desc)) {
91
- debug(`yield section ${OL(desc)}`);
92
- yield this.section(desc);
93
- } else if (isSetName(desc)) {
94
- debug(`expand set ${OL(desc)}`);
95
- debug('hSets', this.hSets);
96
- lNames = this.hSets[desc];
97
- assert(defined(lNames), `no set named ${OL(desc)}`);
98
- debug(`set ${desc}`, lNames);
99
- for (i = 0, len = lNames.length; i < len; i++) {
100
- name = lNames[i];
101
- debug(`yield section ${OL(desc)}`);
102
- yield this.section(name);
103
- }
104
- } else {
105
- croak(`bad name ${OL(desc)}`);
106
- }
107
- } else {
108
- assert(isArray(desc), "not an array");
109
- for (j = 0, len1 = desc.length; j < len1; j++) {
110
- name = desc[j];
111
- debug(`yield section ${OL(name)}`);
112
- assert(isString(name), `not a string ${OL(name)}`);
113
- if (isSectionName(name)) {
114
- yield this.section(name);
115
- } else if (isSetName(name)) {
116
- ref1 = this.hSets[name];
117
- for (_ in ref1) {
118
- sect = ref1[_];
119
- yield sect;
120
- }
119
+ desc = this.lSectionTree;
120
+ }
121
+ if (isArray(desc)) {
122
+ for (j = 0, len = desc.length; j < len; j++) {
123
+ item = desc[j];
124
+ if (isSectionName(item)) {
125
+ result = [level, this.section(item)];
126
+ debug('yield', result);
127
+ yield result;
128
+ } else if (isSetName(item)) {
129
+ pass;
121
130
  } else {
122
- croak(`bad name ${OL(name)}`);
131
+ assert(isArray(item), `not an array ${OL(item)}`);
132
+ yield* this.allSections(item, level + 1);
123
133
  }
124
134
  }
135
+ } else if (isSectionName(desc)) {
136
+ result = [level, this.section(desc)];
137
+ debug('yield', result);
138
+ yield result;
139
+ } else if (isSetName(desc)) {
140
+ lTree = this.hSets[desc];
141
+ assert(defined(lTree), `Not a Set: ${OL(desc)}`);
142
+ yield* this.allSections(lTree, level);
143
+ } else {
144
+ croak(`Bad item: ${OL(desc)}`);
125
145
  }
126
146
  debug("return from allSections()");
127
147
  }
128
148
 
129
149
  // ..........................................................
130
- addSet(name, lSections) {
131
- var i, len, secName;
132
- debug("enter addSet()", name, lSections);
133
- // --- check the name
134
- assert(isSetName(name), `not a valid set name ${OL(name)}`);
135
- // --- check lSections
136
- assert(isArray(lSections), "arg 2 not an array");
137
- for (i = 0, len = lSections.length; i < len; i++) {
138
- secName = lSections[i];
139
- assert(isNonEmptyString(secName), `not a non-empty string ${OL(secName)}`);
140
- assert(defined(this.hSections[secName]), `not a section name ${OL(secName)}`);
150
+ // --- procFunc should be (name, block) ->
151
+ // return processed block
152
+ getBlock(procFunc = undef, lTree = undef) {
153
+ var j, lParts, len, part, result, text;
154
+ debug("enter getBlock()");
155
+ if (lTree === undef) {
156
+ lTree = this.lSectionTree;
157
+ } else {
158
+ assert(isArray(lTree), `not an array ${OL(lTree)}`);
141
159
  }
142
- this.hSets[name] = lSections;
143
- debug('hSets', this.hSets);
144
- debug("return from addSet()");
160
+ lParts = [];
161
+ for (j = 0, len = lTree.length; j < len; j++) {
162
+ part = lTree[j];
163
+ if (isString(part)) {
164
+ text = this.section(part).getBlock();
165
+ if (nonEmpty(text) && defined(procFunc)) {
166
+ text = procFunc(part, text);
167
+ }
168
+ } else if (isNonEmptyArray(part)) {
169
+ if (isSectionName(part[0])) {
170
+ text = this.getBlock(procFunc, part);
171
+ } else if (isSetName(part[0])) {
172
+ text = this.getBlock(procFunc, part.slice(1));
173
+ if (nonEmpty(text) && defined(procFunc)) {
174
+ text = procFunc(part[0], text);
175
+ }
176
+ } else {
177
+ croak(`Bad part: ${OL(part)}`);
178
+ }
179
+ } else {
180
+ croak(`Bad part: ${OL(part)}`);
181
+ }
182
+ if (defined(text)) {
183
+ lParts.push(text);
184
+ }
185
+ }
186
+ debug('lParts', lParts);
187
+ result = arrayToBlock(lParts);
188
+ debug("return from getBlock()", result);
189
+ return result;
145
190
  }
146
191
 
147
192
  // ..........................................................
@@ -154,30 +199,31 @@ export var SectionMap = class SectionMap {
154
199
 
155
200
  // ..........................................................
156
201
  firstSection(name) {
157
- var lSections;
202
+ var lSectionTree;
158
203
  assert(isSetName(name), `bad set name ${OL(name)}`);
159
- lSections = this.hSets[name];
160
- assert(defined(lSections), `no such set ${OL(name)}`);
161
- assert(nonEmpty(lSections), `empty section ${OL(name)}`);
162
- return this.section(lSections[0]);
204
+ lSectionTree = this.hSets[name];
205
+ assert(defined(lSectionTree), `no such set ${OL(name)}`);
206
+ assert(nonEmpty(lSectionTree), `empty section ${OL(name)}`);
207
+ return this.section(lSectionTree[0]);
163
208
  }
164
209
 
165
210
  // ..........................................................
166
211
  lastSection(name) {
167
- var lSections;
212
+ var lSectionTree;
168
213
  assert(isSetName(name), `bad set name ${OL(name)}`);
169
- lSections = this.hSets[name];
170
- assert(defined(lSections), `no such set ${OL(name)}`);
171
- assert(nonEmpty(lSections), `empty section ${OL(name)}`);
172
- return this.section(lSections[lSections.length - 1]);
214
+ lSectionTree = this.hSets[name];
215
+ assert(defined(lSectionTree), `no such set ${OL(name)}`);
216
+ assert(nonEmpty(lSectionTree), `empty section ${OL(name)}`);
217
+ return this.section(lSectionTree[lSectionTree.length - 1]);
173
218
  }
174
219
 
175
220
  // ..........................................................
176
221
  length(desc = undef) {
177
- var ref, result, sect;
222
+ var _, ref, result, sect, x;
178
223
  result = 0;
179
224
  ref = this.allSections(desc);
180
- for (sect of ref) {
225
+ for (x of ref) {
226
+ [_, sect] = x;
181
227
  result += sect.length();
182
228
  }
183
229
  return result;
@@ -194,64 +240,18 @@ export var SectionMap = class SectionMap {
194
240
  }
195
241
 
196
242
  // ..........................................................
197
- indent(desc = undef, level = 1) {
198
- var ref, sect;
199
- ref = this.allSections(desc);
200
- for (sect of ref) {
201
- sect.indent(level);
202
- }
203
- }
204
-
205
- // ..........................................................
206
- enclose(name, pre, post) {
207
- var sect;
208
- if (isSectionName(name)) {
209
- sect = this.section(name);
210
- if (sect.nonEmpty()) {
211
- sect.indent();
212
- sect.prepend(pre);
213
- sect.add(post);
214
- }
215
- } else if (isSetName(name)) {
216
- if (this.nonEmpty(name)) {
217
- this.indent(name);
218
- this.firstSection(name).prepend(pre);
219
- this.lastSection(name).add(post);
220
- }
221
- } else {
222
- croak(`Bad name param ${OL(name)}`);
243
+ getShape() {
244
+ var lParts, level, ref, result, sect, x;
245
+ debug("enter getShape()");
246
+ lParts = [];
247
+ ref = this.allSections();
248
+ for (x of ref) {
249
+ [level, sect] = x;
250
+ lParts.push(indented(sect.name, level));
223
251
  }
224
- }
225
-
226
- // ..........................................................
227
- getBlock() {
228
- var result;
229
- debug("enter getBlock()");
230
- this.lAllBlocks = [];
231
- debug('lSections', this.lSections);
232
- this.accumBlock(this.lSections);
233
- debug('lAllBlocks', this.lAllBlocks);
234
- result = arrayToBlock(this.lAllBlocks);
235
- debug("return from getBlock()", result);
252
+ result = arrayToBlock(lParts);
253
+ debug("return from getShape()", result);
236
254
  return result;
237
255
  }
238
256
 
239
- // ..........................................................
240
- accumBlock(tree) {
241
- var block, i, len, subtree;
242
- if (isString(tree)) {
243
- debug(`accumBlock ${OL(tree)}`);
244
- block = this.section(tree).getBlock();
245
- if (nonEmpty(block)) {
246
- this.lAllBlocks.push(block);
247
- }
248
- } else {
249
- assert(isArray(tree), "not an array");
250
- for (i = 0, len = tree.length; i < len; i++) {
251
- subtree = tree[i];
252
- this.accumBlock(subtree);
253
- }
254
- }
255
- }
256
-
257
257
  };
@@ -30,19 +30,31 @@ export blockToArray = (block) ->
30
30
  export arrayToBlock = (lLines) ->
31
31
 
32
32
  if (lLines == undef)
33
- return ''
33
+ return undef
34
34
  assert isArray(lLines), "lLines is not an array"
35
35
  lLines = lLines.filter((line) => defined(line));
36
36
  if lLines.length == 0
37
- return ''
37
+ return undef
38
38
  else
39
39
  return rtrim(lLines.join('\n'))
40
40
 
41
41
  # ---------------------------------------------------------------------------
42
42
 
43
+ export splitBlock = (block) ->
44
+
45
+ assert isString(block), "not a string"
46
+ pos = block.indexOf('\n')
47
+ if (pos == -1)
48
+ return [block, undef]
49
+ else
50
+ return [block.substring(0, pos), block.substring(pos+1)]
51
+
52
+
53
+ # ---------------------------------------------------------------------------
54
+
43
55
  export firstLine = (block) ->
44
56
 
45
- assert isString(block), "firstLine(): string expected"
57
+ assert isString(block), "not a string"
46
58
  pos = block.indexOf('\n')
47
59
  if (pos == -1)
48
60
  return block
@@ -53,10 +65,10 @@ export firstLine = (block) ->
53
65
 
54
66
  export remainingLines = (block) ->
55
67
 
56
- assert isString(block), "remainingLines(): string expected"
68
+ assert isString(block), "not a string"
57
69
  pos = block.indexOf('\n')
58
70
  if (pos == -1)
59
- return ''
71
+ return undef
60
72
  else
61
73
  return block.substring(pos+1)
62
74
 
@@ -38,23 +38,35 @@ export var blockToArray = function(block) {
38
38
  // arrayToBlock - block will have no trailing whitespace
39
39
  export var arrayToBlock = function(lLines) {
40
40
  if (lLines === undef) {
41
- return '';
41
+ return undef;
42
42
  }
43
43
  assert(isArray(lLines), "lLines is not an array");
44
44
  lLines = lLines.filter((line) => {
45
45
  return defined(line);
46
46
  });
47
47
  if (lLines.length === 0) {
48
- return '';
48
+ return undef;
49
49
  } else {
50
50
  return rtrim(lLines.join('\n'));
51
51
  }
52
52
  };
53
53
 
54
+ // ---------------------------------------------------------------------------
55
+ export var splitBlock = function(block) {
56
+ var pos;
57
+ assert(isString(block), "not a string");
58
+ pos = block.indexOf('\n');
59
+ if (pos === -1) {
60
+ return [block, undef];
61
+ } else {
62
+ return [block.substring(0, pos), block.substring(pos + 1)];
63
+ }
64
+ };
65
+
54
66
  // ---------------------------------------------------------------------------
55
67
  export var firstLine = function(block) {
56
68
  var pos;
57
- assert(isString(block), "firstLine(): string expected");
69
+ assert(isString(block), "not a string");
58
70
  pos = block.indexOf('\n');
59
71
  if (pos === -1) {
60
72
  return block;
@@ -66,10 +78,10 @@ export var firstLine = function(block) {
66
78
  // ---------------------------------------------------------------------------
67
79
  export var remainingLines = function(block) {
68
80
  var pos;
69
- assert(isString(block), "remainingLines(): string expected");
81
+ assert(isString(block), "not a string");
70
82
  pos = block.indexOf('\n');
71
83
  if (pos === -1) {
72
- return '';
84
+ return undef;
73
85
  } else {
74
86
  return block.substring(pos + 1);
75
87
  }
@@ -173,6 +173,12 @@ export isArray = (x) ->
173
173
 
174
174
  # ---------------------------------------------------------------------------
175
175
 
176
+ export isNonEmptyArray = (x) ->
177
+
178
+ return isArray(x) && (x.length > 0)
179
+
180
+ # ---------------------------------------------------------------------------
181
+
176
182
  export isHash = (x, lKeys) ->
177
183
 
178
184
  if ! x || (getClassName(x) != 'Object')
@@ -186,6 +192,12 @@ export isHash = (x, lKeys) ->
186
192
 
187
193
  # ---------------------------------------------------------------------------
188
194
 
195
+ export isNonEmptyHash = (x) ->
196
+
197
+ return isHash(x) && (Object.keys(x).length > 0)
198
+
199
+ # ---------------------------------------------------------------------------
200
+
189
201
  export hashHasKey = (x, key) ->
190
202
 
191
203
  assert isHash(x), "hashHasKey(): not a hash"
@@ -159,6 +159,11 @@ export var isArray = function(x) {
159
159
  return Array.isArray(x);
160
160
  };
161
161
 
162
+ // ---------------------------------------------------------------------------
163
+ export var isNonEmptyArray = function(x) {
164
+ return isArray(x) && (x.length > 0);
165
+ };
166
+
162
167
  // ---------------------------------------------------------------------------
163
168
  export var isHash = function(x, lKeys) {
164
169
  var i, key, len;
@@ -177,6 +182,11 @@ export var isHash = function(x, lKeys) {
177
182
  return true;
178
183
  };
179
184
 
185
+ // ---------------------------------------------------------------------------
186
+ export var isNonEmptyHash = function(x) {
187
+ return isHash(x) && (Object.keys(x).length > 0);
188
+ };
189
+
180
190
  // ---------------------------------------------------------------------------
181
191
  export var hashHasKey = function(x, key) {
182
192
  assert(isHash(x), "hashHasKey(): not a hash");
@@ -146,3 +146,10 @@ export tabify = (str, numSpaces=undef) ->
146
146
  export untabify = (str, numSpaces=3) ->
147
147
 
148
148
  return str.replace(/\t/g, ' '.repeat(numSpaces))
149
+
150
+ # ---------------------------------------------------------------------------
151
+ # enclose - indent text, surround with pre and post
152
+
153
+ export enclose = (text, pre, post) ->
154
+
155
+ return pre + "\n" + indented(text) + "\n" + post
@@ -175,3 +175,9 @@ export var tabify = function(str, numSpaces = undef) {
175
175
  export var untabify = function(str, numSpaces = 3) {
176
176
  return str.replace(/\t/g, ' '.repeat(numSpaces));
177
177
  };
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // enclose - indent text, surround with pre and post
181
+ export var enclose = function(text, pre, post) {
182
+ return pre + "\n" + indented(text) + "\n" + post;
183
+ };