@jdeighan/coffee-utils 8.0.1 → 8.0.4

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.1",
4
+ "version": "8.0.4",
5
5
  "description": "A set of utility functions for CoffeeScript",
6
6
  "main": "coffee_utils.js",
7
7
  "exports": {
@@ -19,6 +19,8 @@
19
19
  "./store": "./src/DataStores.js",
20
20
  "./taml": "./src/taml.js",
21
21
  "./placeholders": "./src/placeholders.js",
22
+ "./section": "./src/Section.js",
23
+ "./sectionmap": "./src/SectionMap.js",
22
24
  "./package.json": "./package.json"
23
25
  },
24
26
  "engines": {
@@ -53,6 +55,6 @@
53
55
  "svelte": "^3.48.0"
54
56
  },
55
57
  "devDependencies": {
56
- "@jdeighan/unit-tester": "^2.0.8"
58
+ "@jdeighan/unit-tester": "^2.0.9"
57
59
  }
58
60
  }
@@ -0,0 +1,69 @@
1
+ # Section.coffee
2
+
3
+ import {
4
+ assert, pass, undef, defined, croak, isArray,
5
+ } from '@jdeighan/coffee-utils'
6
+ import {arrayToBlock} from '@jdeighan/coffee-utils/block'
7
+ import {indented} from '@jdeighan/coffee-utils/indent'
8
+
9
+ # ---------------------------------------------------------------------------
10
+
11
+ export class Section
12
+
13
+ constructor: () ->
14
+
15
+ @lLines = []
16
+
17
+ # ..........................................................
18
+
19
+ length: () ->
20
+
21
+ return @lLines.length
22
+
23
+ # ..........................................................
24
+
25
+ indent: (level=1) ->
26
+
27
+ lNewLines = for line in @lLines
28
+ indented(line, level)
29
+ @lLines = lNewLines
30
+ return
31
+
32
+ # ..........................................................
33
+
34
+ isEmpty: () ->
35
+
36
+ return (@lLines.length == 0)
37
+
38
+ # ..........................................................
39
+
40
+ nonEmpty: () ->
41
+
42
+ return (@lLines.length > 0)
43
+
44
+ # ..........................................................
45
+
46
+ add: (data) ->
47
+
48
+ if isArray(data)
49
+ for line in data
50
+ @lLines.push line
51
+ else
52
+ @lLines.push data
53
+ return
54
+
55
+ # ..........................................................
56
+
57
+ prepend: (data) ->
58
+
59
+ if isArray(data)
60
+ @lLines = [data..., @lLines...]
61
+ else
62
+ @lLines = [data, @lLines...]
63
+ return
64
+
65
+ # ..........................................................
66
+
67
+ getBlock: () ->
68
+
69
+ return arrayToBlock(@lLines)
package/src/Section.js ADDED
@@ -0,0 +1,84 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // Section.coffee
3
+ import {
4
+ assert,
5
+ pass,
6
+ undef,
7
+ defined,
8
+ croak,
9
+ isArray
10
+ } from '@jdeighan/coffee-utils';
11
+
12
+ import {
13
+ arrayToBlock
14
+ } from '@jdeighan/coffee-utils/block';
15
+
16
+ import {
17
+ indented
18
+ } from '@jdeighan/coffee-utils/indent';
19
+
20
+ // ---------------------------------------------------------------------------
21
+ export var Section = class Section {
22
+ constructor() {
23
+ this.lLines = [];
24
+ }
25
+
26
+ // ..........................................................
27
+ length() {
28
+ return this.lLines.length;
29
+ }
30
+
31
+ // ..........................................................
32
+ indent(level = 1) {
33
+ var lNewLines, line;
34
+ lNewLines = (function() {
35
+ var i, len, ref, results;
36
+ ref = this.lLines;
37
+ results = [];
38
+ for (i = 0, len = ref.length; i < len; i++) {
39
+ line = ref[i];
40
+ results.push(indented(line, level));
41
+ }
42
+ return results;
43
+ }).call(this);
44
+ this.lLines = lNewLines;
45
+ }
46
+
47
+ // ..........................................................
48
+ isEmpty() {
49
+ return this.lLines.length === 0;
50
+ }
51
+
52
+ // ..........................................................
53
+ nonEmpty() {
54
+ return this.lLines.length > 0;
55
+ }
56
+
57
+ // ..........................................................
58
+ add(data) {
59
+ var i, len, line;
60
+ if (isArray(data)) {
61
+ for (i = 0, len = data.length; i < len; i++) {
62
+ line = data[i];
63
+ this.lLines.push(line);
64
+ }
65
+ } else {
66
+ this.lLines.push(data);
67
+ }
68
+ }
69
+
70
+ // ..........................................................
71
+ prepend(data) {
72
+ if (isArray(data)) {
73
+ this.lLines = [...data, ...this.lLines];
74
+ } else {
75
+ this.lLines = [data, ...this.lLines];
76
+ }
77
+ }
78
+
79
+ // ..........................................................
80
+ getBlock() {
81
+ return arrayToBlock(this.lLines);
82
+ }
83
+
84
+ };
@@ -0,0 +1,221 @@
1
+ # SectionMap.coffee
2
+
3
+ import {
4
+ assert, pass, undef, defined, croak, OL, isEmpty, nonEmpty,
5
+ isString, isHash, isArray, isUniqueTree, isNonEmptyString,
6
+ } from '@jdeighan/coffee-utils'
7
+ import {arrayToBlock} from '@jdeighan/coffee-utils/block'
8
+ import {debug} from '@jdeighan/coffee-utils/debug'
9
+ import {Section} from '@jdeighan/coffee-utils/section'
10
+
11
+ # ---------------------------------------------------------------------------
12
+
13
+ isSectionName = (name) ->
14
+
15
+ assert isString(name), "not a string"
16
+ return name.match(/^[a-z][a-z0-9]*/)
17
+
18
+ # ---------------------------------------------------------------------------
19
+
20
+ isSetName = (name) ->
21
+
22
+ assert isString(name), "not a string"
23
+ return name.match(/^[A-Z][a-z0-9]*/)
24
+
25
+ # ---------------------------------------------------------------------------
26
+
27
+ export class SectionMap
28
+
29
+ constructor: (lSections) ->
30
+ # --- lSections is a tree of section names
31
+
32
+ debug "enter SectionMap()", lSections
33
+ @lSections = lSections # a tree of section names
34
+ @hSets = {}
35
+ @hSections = {}
36
+ @addSections lSections
37
+ debug "return from SectionMap()", @hSections
38
+
39
+ # ..........................................................
40
+
41
+ # --- TODO: Allow array to start with a set name ---
42
+
43
+ addSections: (desc) ->
44
+
45
+ if isString(desc)
46
+ assert nonEmpty(desc), "empty section name"
47
+ assert desc.match(/^[a-z][a-z0-9]*$/), "bad section name #{OL(desc)}"
48
+ assert (@hSections[desc] == undef), "duplicate section #{OL(desc)}"
49
+ @hSections[desc] = new Section()
50
+ else
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
92
+ else
93
+ croak "bad name #{OL(name)}"
94
+ debug "return from allSections()"
95
+ return
96
+
97
+ # ..........................................................
98
+
99
+ addSet: (name, lSections) ->
100
+
101
+ debug "enter addSet()", name, lSections
102
+
103
+ # --- check the name
104
+ assert isSetName(name), "not a valid set name #{OL(name)}"
105
+
106
+ # --- check lSections
107
+ assert isArray(lSections), "arg 2 not an array"
108
+ for secName in lSections
109
+ assert isNonEmptyString(secName),
110
+ "not a non-empty string #{OL(secName)}"
111
+ assert defined(@hSections[secName]),
112
+ "not a section name #{OL(secName)}"
113
+
114
+ @hSets[name] = lSections
115
+ debug 'hSets', @hSets
116
+ debug "return from addSet()"
117
+ return
118
+
119
+ # ..........................................................
120
+
121
+ section: (name) ->
122
+
123
+ sect = @hSections[name]
124
+ assert defined(sect), "No section named #{OL(name)}"
125
+ return sect
126
+
127
+ # ..........................................................
128
+
129
+ firstSection: (name) ->
130
+
131
+ 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])
136
+
137
+ # ..........................................................
138
+
139
+ lastSection: (name) ->
140
+
141
+ 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])
146
+
147
+ # ..........................................................
148
+
149
+ length: (desc=undef) ->
150
+
151
+ result = 0
152
+ for sect from @allSections(desc)
153
+ result += sect.length()
154
+ return result
155
+
156
+ # ..........................................................
157
+
158
+ isEmpty: (desc=undef) ->
159
+
160
+ return (@length(desc) == 0)
161
+
162
+ # ..........................................................
163
+
164
+ nonEmpty: (desc=undef) ->
165
+
166
+ return (@length(desc) > 0)
167
+
168
+ # ..........................................................
169
+
170
+ indent: (desc=undef, level=1) ->
171
+
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
206
+ 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
@@ -0,0 +1,257 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // SectionMap.coffee
3
+ var isSectionName, isSetName;
4
+
5
+ import {
6
+ assert,
7
+ pass,
8
+ undef,
9
+ defined,
10
+ croak,
11
+ OL,
12
+ isEmpty,
13
+ nonEmpty,
14
+ isString,
15
+ isHash,
16
+ isArray,
17
+ isUniqueTree,
18
+ isNonEmptyString
19
+ } from '@jdeighan/coffee-utils';
20
+
21
+ import {
22
+ arrayToBlock
23
+ } from '@jdeighan/coffee-utils/block';
24
+
25
+ import {
26
+ debug
27
+ } from '@jdeighan/coffee-utils/debug';
28
+
29
+ import {
30
+ Section
31
+ } from '@jdeighan/coffee-utils/section';
32
+
33
+ // ---------------------------------------------------------------------------
34
+ isSectionName = function(name) {
35
+ assert(isString(name), "not a string");
36
+ return name.match(/^[a-z][a-z0-9]*/);
37
+ };
38
+
39
+ // ---------------------------------------------------------------------------
40
+ isSetName = function(name) {
41
+ assert(isString(name), "not a string");
42
+ return name.match(/^[A-Z][a-z0-9]*/);
43
+ };
44
+
45
+ // ---------------------------------------------------------------------------
46
+ 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
51
+ this.hSets = {};
52
+ this.hSections = {};
53
+ this.addSections(lSections);
54
+ debug("return from SectionMap()", this.hSections);
55
+ }
56
+
57
+ // ..........................................................
58
+
59
+ // --- TODO: Allow array to start with a set name ---
60
+ addSections(desc) {
61
+ var i, item, len;
62
+ if (isString(desc)) {
63
+ assert(nonEmpty(desc), "empty section name");
64
+ assert(desc.match(/^[a-z][a-z0-9]*$/), `bad section name ${OL(desc)}`);
65
+ assert(this.hSections[desc] === undef, `duplicate section ${OL(desc)}`);
66
+ this.hSections[desc] = new Section();
67
+ } else {
68
+ assert(isArray(desc), `not an array or string ${OL(desc)}`);
69
+ for (i = 0, len = desc.length; i < len; i++) {
70
+ item = desc[i];
71
+ this.addSections(item);
72
+ }
73
+ }
74
+ }
75
+
76
+ // ..........................................................
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()");
81
+ 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
+ }
121
+ } else {
122
+ croak(`bad name ${OL(name)}`);
123
+ }
124
+ }
125
+ }
126
+ debug("return from allSections()");
127
+ }
128
+
129
+ // ..........................................................
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)}`);
141
+ }
142
+ this.hSets[name] = lSections;
143
+ debug('hSets', this.hSets);
144
+ debug("return from addSet()");
145
+ }
146
+
147
+ // ..........................................................
148
+ section(name) {
149
+ var sect;
150
+ sect = this.hSections[name];
151
+ assert(defined(sect), `No section named ${OL(name)}`);
152
+ return sect;
153
+ }
154
+
155
+ // ..........................................................
156
+ firstSection(name) {
157
+ var lSections;
158
+ 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]);
163
+ }
164
+
165
+ // ..........................................................
166
+ lastSection(name) {
167
+ var lSections;
168
+ 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]);
173
+ }
174
+
175
+ // ..........................................................
176
+ length(desc = undef) {
177
+ var ref, result, sect;
178
+ result = 0;
179
+ ref = this.allSections(desc);
180
+ for (sect of ref) {
181
+ result += sect.length();
182
+ }
183
+ return result;
184
+ }
185
+
186
+ // ..........................................................
187
+ isEmpty(desc = undef) {
188
+ return this.length(desc) === 0;
189
+ }
190
+
191
+ // ..........................................................
192
+ nonEmpty(desc = undef) {
193
+ return this.length(desc) > 0;
194
+ }
195
+
196
+ // ..........................................................
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)}`);
223
+ }
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);
236
+ return result;
237
+ }
238
+
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
+ };
@@ -296,6 +296,26 @@ export isUniqueList = (lItems, func=undef) ->
296
296
 
297
297
  # ---------------------------------------------------------------------------
298
298
 
299
+ export isUniqueTree = (lItems, func=undef, hFound={}) ->
300
+
301
+ if isEmpty(lItems)
302
+ return true # empty list is unique
303
+ if defined(func)
304
+ assert isFunction(func), "Not a function: #{OL(func)}"
305
+ for item in lItems
306
+ if isArray(item)
307
+ if ! isUniqueTree(item, func, hFound)
308
+ return false
309
+ else
310
+ if defined(func) && !func(item)
311
+ return false
312
+ if defined(hFound[item])
313
+ return false
314
+ hFound[item] = 1
315
+ return true
316
+
317
+ # ---------------------------------------------------------------------------
318
+
299
319
  export uniq = (lItems) ->
300
320
 
301
321
  return [...new Set(lItems)]
@@ -303,6 +303,34 @@ export var isUniqueList = function(lItems, func = undef) {
303
303
  return true;
304
304
  };
305
305
 
306
+ // ---------------------------------------------------------------------------
307
+ export var isUniqueTree = function(lItems, func = undef, hFound = {}) {
308
+ var i, item, len;
309
+ if (isEmpty(lItems)) {
310
+ return true; // empty list is unique
311
+ }
312
+ if (defined(func)) {
313
+ assert(isFunction(func), `Not a function: ${OL(func)}`);
314
+ }
315
+ for (i = 0, len = lItems.length; i < len; i++) {
316
+ item = lItems[i];
317
+ if (isArray(item)) {
318
+ if (!isUniqueTree(item, func, hFound)) {
319
+ return false;
320
+ }
321
+ } else {
322
+ if (defined(func) && !func(item)) {
323
+ return false;
324
+ }
325
+ if (defined(hFound[item])) {
326
+ return false;
327
+ }
328
+ hFound[item] = 1;
329
+ }
330
+ }
331
+ return true;
332
+ };
333
+
306
334
  // ---------------------------------------------------------------------------
307
335
  export var uniq = function(lItems) {
308
336
  return [...new Set(lItems)];