@jdeighan/coffee-utils 8.0.1 → 8.0.4

Sign up to get free protection for your applications and to get access to all the features.
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)];