@jdeighan/coffee-utils 7.0.50 → 7.0.53

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  // Generated by CoffeeScript 2.7.0
2
2
  // debug_utils.coffee
3
- var adjustStack, doDebugDebug, reMethod, resetDebugging, shouldLogFunc, shouldLogString, stack;
3
+ var callStack, doDebugDebug, reMethod, resetDebugging;
4
4
 
5
5
  import {
6
6
  assert,
@@ -12,6 +12,7 @@ import {
12
12
  isString,
13
13
  isFunction,
14
14
  isBoolean,
15
+ sep_dash,
15
16
  OL,
16
17
  escapeStr,
17
18
  isNumber,
@@ -36,28 +37,170 @@ import {
36
37
  CallStack
37
38
  } from '@jdeighan/coffee-utils/stack';
38
39
 
40
+ import {
41
+ getPrefix,
42
+ addArrow,
43
+ removeLastVbar
44
+ } from '@jdeighan/coffee-utils/arrow';
45
+
39
46
  import {
40
47
  log,
41
48
  logItem,
42
49
  LOG,
43
- setStringifier,
44
- orderedStringify
50
+ shortEnough
45
51
  } from '@jdeighan/coffee-utils/log';
46
52
 
47
- // --- These are saved/restored on the call stack
48
- export var debugging = false;
53
+ callStack = new CallStack();
49
54
 
50
- shouldLogFunc = function(func) {
51
- return debugging;
52
- };
55
+ doDebugDebug = false;
56
+
57
+ export var shouldLog = undef; // set in resetDebugging() and setDebugging()
58
+
59
+ export var lFuncList = [];
53
60
 
54
- shouldLogString = function(str) {
55
- return debugging;
61
+ // ---------------------------------------------------------------------------
62
+ export var debug = function(label, ...lObjects) {
63
+ var doLog, funcName, i, j, k, l, len1, len2, len3, level, nObjects, obj, prefix, type;
64
+ assert(isString(label), `1st arg ${OL(label)} should be a string`);
65
+ // --- We want to allow objects to be undef. Therefore, we need to
66
+ // distinguish between 1 arg sent vs. 2 or more args sent
67
+ nObjects = lObjects.length;
68
+ // --- funcName is only set for types 'enter' and 'return'
69
+ [type, funcName] = getType(label, nObjects);
70
+ if (doDebugDebug) {
71
+ LOG(`debug(): type = ${OL(type)}`);
72
+ LOG(`debug(): funcName = ${OL(funcName)}`);
73
+ }
74
+ switch (type) {
75
+ case 'enter':
76
+ callStack.enter(funcName);
77
+ label = shouldLog(label, type, funcName, callStack);
78
+ break;
79
+ case 'return':
80
+ label = shouldLog(label, type, funcName, callStack);
81
+ break;
82
+ case 'string':
83
+ label = shouldLog(label, type, undef, callStack);
84
+ assert(nObjects === 0, `multiple objects only not allowed for ${OL(type)}`);
85
+ break;
86
+ case 'objects':
87
+ label = shouldLog(label, type, undef, callStack);
88
+ assert(nObjects > 0, `multiple objects only not allowed for ${OL(type)}`);
89
+ }
90
+ doLog = defined(label);
91
+ if (doDebugDebug) {
92
+ if (nObjects === 0) {
93
+ LOG(`debug(${OL(label)}) - 1 arg`);
94
+ } else {
95
+ LOG(`debug(${OL(label)}), ${nObjects} args`);
96
+ }
97
+ LOG(`doLog = ${OL(doLog)}`);
98
+ }
99
+ if (doLog) {
100
+ level = callStack.getLevel();
101
+ prefix = getPrefix(level);
102
+ if (doDebugDebug) {
103
+ LOG("callStack", callStack);
104
+ LOG(`level = ${OL(level)}`);
105
+ LOG(`prefix = ${OL(prefix)}`);
106
+ }
107
+ switch (type) {
108
+ case 'enter':
109
+ log(label, {prefix});
110
+ for (i = j = 0, len1 = lObjects.length; j < len1; i = ++j) {
111
+ obj = lObjects[i];
112
+ if (i > 0) {
113
+ log(sep_dash, {
114
+ prefix: removeLastVbar(prefix)
115
+ });
116
+ }
117
+ logItem(undef, obj, {
118
+ prefix: removeLastVbar(prefix)
119
+ });
120
+ }
121
+ break;
122
+ case 'return':
123
+ log(label, {
124
+ prefix: addArrow(prefix)
125
+ });
126
+ for (i = k = 0, len2 = lObjects.length; k < len2; i = ++k) {
127
+ obj = lObjects[i];
128
+ if (i > 0) {
129
+ log(sep_dash, {
130
+ prefix: removeLastVbar(prefix)
131
+ });
132
+ }
133
+ logItem(undef, obj, {
134
+ prefix: removeLastVbar(prefix)
135
+ });
136
+ }
137
+ break;
138
+ case 'string':
139
+ log(label, {prefix});
140
+ break;
141
+ case 'objects':
142
+ if ((nObjects === 1) && shortEnough(label, lObjects[0])) {
143
+ logItem(label, lObjects[0], {prefix});
144
+ } else {
145
+ if (label.indexOf(':') !== label.length - 1) {
146
+ label += ':';
147
+ }
148
+ log(label, {prefix});
149
+ for (l = 0, len3 = lObjects.length; l < len3; l++) {
150
+ obj = lObjects[l];
151
+ logItem(undef, obj, {prefix});
152
+ }
153
+ }
154
+ }
155
+ }
156
+ if ((type === 'enter') && doLog) {
157
+ callStack.logCurFunc();
158
+ } else if (type === 'return') {
159
+ callStack.returnFrom(funcName);
160
+ }
161
+ return true; // allow use in boolean expressions
56
162
  };
57
163
 
58
- stack = new CallStack();
59
164
 
60
- doDebugDebug = false;
165
+ // ---------------------------------------------------------------------------
166
+ export var stdShouldLog = function(label, type, funcName, stack) {
167
+ // --- if type is 'enter', then funcName won't be on the stack yet
168
+ // returns the (possibly modified) label to log
169
+
170
+ // --- If we're logging now,
171
+ // but we won't be logging when funcName is activated
172
+ // then change 'enter' to 'call'
173
+ assert(isString(label), `label ${OL(label)} not a string`);
174
+ assert(isString(type), `type ${OL(type)} not a string`);
175
+ if ((type === 'enter') || (type === 'return')) {
176
+ assert(isString(funcName), `func name ${OL(funcName)} not a string`);
177
+ } else {
178
+ assert(funcName === undef, `func name ${OL(funcName)} not undef`);
179
+ }
180
+ assert(stack instanceof CallStack, "not a call stack object");
181
+ if (doDebugDebug) {
182
+ LOG(`stdShouldLog(${OL(label)}, ${OL(type)}, ${OL(funcName)}, stack)`);
183
+ LOG("stack", stack);
184
+ LOG("lFuncList", lFuncList);
185
+ }
186
+ switch (type) {
187
+ case 'enter':
188
+ if (funcMatch(stack, lFuncList)) {
189
+ return label;
190
+ // --- As a special case, if we enter a function where we will not
191
+ // be logging, but we were logging in the calling function,
192
+ // we'll log out the call itself
193
+ } else if (stack.isLoggingPrev()) {
194
+ return label.replace('enter', 'call');
195
+ }
196
+ break;
197
+ default:
198
+ if (funcMatch(stack, lFuncList)) {
199
+ return label;
200
+ }
201
+ }
202
+ return undef;
203
+ };
61
204
 
62
205
  // ---------------------------------------------------------------------------
63
206
  export var debugDebug = function(flag = true) {
@@ -69,177 +212,137 @@ export var debugDebug = function(flag = true) {
69
212
 
70
213
  // ---------------------------------------------------------------------------
71
214
  resetDebugging = function() {
72
- debugging = false;
73
215
  if (doDebugDebug) {
74
- LOG("resetDebugging() - debugging = false");
216
+ LOG("resetDebugging()");
75
217
  }
76
- stack.reset();
77
- shouldLogFunc = function(func) {
78
- return debugging;
79
- };
80
- shouldLogString = function(str) {
81
- return debugging;
218
+ callStack.reset();
219
+ shouldLog = function(label, type, funcName, stack) {
220
+ return undef;
82
221
  };
83
222
  };
84
223
 
85
224
  // ---------------------------------------------------------------------------
86
- export var setDebugging = function(funcDoDebug = undef, funcDoLog = undef) {
87
- var lFuncNames;
225
+ export var setDebugging = function(option) {
88
226
  resetDebugging();
89
- if (isBoolean(funcDoDebug)) {
90
- debugging = funcDoDebug;
227
+ if (isBoolean(option)) {
228
+ if (option) {
229
+ shouldLog = function(label, type, funcName, stack) {
230
+ return label;
231
+ };
232
+ } else {
233
+ shouldLog = function(label, type, funcName, stack) {
234
+ return undef;
235
+ };
236
+ }
91
237
  if (doDebugDebug) {
92
- LOG(`setDebugging(): debugging = ${funcDoDebug}`);
238
+ LOG(`setDebugging = ${option}`);
93
239
  }
94
- } else if (isString(funcDoDebug)) {
95
- debugging = false;
96
- lFuncNames = words(funcDoDebug);
97
- assert(isArray(lFuncNames), `words('${funcDoDebug}') returned non-array`);
98
- shouldLogFunc = function(funcName) {
99
- return funcMatch(funcName, lFuncNames);
100
- };
240
+ } else if (isString(option)) {
241
+ lFuncList = getFuncList(option);
242
+ shouldLog = stdShouldLog;
101
243
  if (doDebugDebug) {
102
- LOG(`setDebugging FUNCS: ${lFuncNames.join(',')}, debugging = false`);
244
+ LOG(`setDebugging FUNCS: ${option}`);
245
+ LOG('lFuncList', lFuncList);
103
246
  }
104
- } else if (isFunction(funcDoDebug)) {
105
- shouldLogFunc = funcDoDebug;
247
+ } else if (isFunction(option)) {
248
+ shouldLog = option;
106
249
  if (doDebugDebug) {
107
250
  LOG("setDebugging to custom func");
108
251
  }
109
252
  } else {
110
- croak(`setDebugging(): bad parameter ${OL(funcDoDebug)}`);
111
- }
112
- if (isFunction(funcDoLog)) {
113
- assert(isFunction(funcDoLog), "setDebugging: arg 2 not a function");
114
- shouldLogString = funcDoLog;
253
+ croak(`bad parameter ${OL(option)}`);
115
254
  }
116
255
  };
117
256
 
118
257
  // ---------------------------------------------------------------------------
119
258
  // --- export only to allow unit tests
120
- export var funcMatch = function(curFunc, lFuncNames) {
121
- var _, cls, lMatches, meth;
122
- assert(isString(curFunc), "funcMatch(): not a string");
123
- assert(isArray(lFuncNames), `funcMatch(): bad array ${lFuncNames}`);
124
- if (lFuncNames.includes(curFunc)) {
125
- return true;
126
- } else if ((lMatches = curFunc.match(reMethod)) && ([_, cls, meth] = lMatches) && lFuncNames.includes(meth)) {
127
- return true;
128
- } else {
129
- return false;
130
- }
131
- };
132
-
133
- // ---------------------------------------------------------------------------
134
- // 1. adjust call stack on 'enter' or 'return from'
135
- // 2. adjust debugging flag
136
- // 3. return [mainPrefix, auxPrefix, hEnv, type] - hEnv can be undef
137
- // 4. disable logging by setting type to undef
138
- adjustStack = function(str) {
139
- var _, auxPre, curFunc, hEnv, lMatches, mainPre, trans;
140
- if ((lMatches = str.match(/^\s*enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/))) {
141
- // --- We are entering function curFunc
142
- curFunc = lMatches[1];
143
- hEnv = {debugging, shouldLogFunc, shouldLogString};
144
- debugging = shouldLogFunc(curFunc);
145
- if (doDebugDebug) {
146
- trans = `${hEnv.debugging} => ${debugging}`;
147
- LOG(` ENTER ${curFunc}, debugging: ${trans}`);
148
- }
149
- [mainPre, auxPre] = stack.doCall(curFunc, hEnv, debugging);
150
- return [mainPre, auxPre, undef, shouldLogFunc(curFunc) ? 'enter' : undef];
151
- } else if ((lMatches = str.match(/^\s*return.+from\s+([A-Za-z_][A-Za-z0-9_\.]*)/))) {
152
- curFunc = lMatches[1];
153
- [mainPre, auxPre, hEnv] = stack.returnFrom(curFunc);
154
- if (doDebugDebug) {
155
- LOG(` RETURN FROM ${curFunc}`);
259
+ export var getFuncList = function(str) {
260
+ var _, ident1, ident2, j, lMatches, len1, plus, ref, word;
261
+ lFuncList = [];
262
+ ref = words(str);
263
+ for (j = 0, len1 = ref.length; j < len1; j++) {
264
+ word = ref[j];
265
+ if (lMatches = word.match(/^([A-Za-z_][A-Za-z0-9_]*)(?:\.([A-Za-z_][A-Za-z0-9_]*))?(\+)?$/)) {
266
+ [_, ident1, ident2, plus] = lMatches;
267
+ if (ident2) {
268
+ lFuncList.push({
269
+ name: ident2,
270
+ object: ident1,
271
+ plus: plus === '+'
272
+ });
273
+ } else {
274
+ lFuncList.push({
275
+ name: ident1,
276
+ plus: plus === '+'
277
+ });
278
+ }
279
+ } else {
280
+ croak(`Bad word in func list: ${OL(word)}`);
156
281
  }
157
- return [mainPre, auxPre, hEnv, shouldLogFunc(curFunc) ? 'return' : undef];
158
- } else {
159
- [mainPre, auxPre, _] = stack.logStr();
160
- return [mainPre, auxPre, undef, shouldLogString(str) ? 'string' : undef];
161
282
  }
283
+ return lFuncList;
162
284
  };
163
285
 
164
286
  // ---------------------------------------------------------------------------
165
- export var debug = function(...lArgs) {
166
- var auxPre, hEnv, hOptions, item, lResult, label, mainPre, nArgs, orgDebugging, trans, type;
167
- // --- We want to allow item to be undef. Therefore, we need to
168
- // distinguish between 1 arg sent vs. 2 args sent
169
- nArgs = lArgs.length;
170
- assert((nArgs === 1) || (nArgs === 2), `debug(): ${nArgs} args`);
171
- // --- label must always be there, and be a string
172
- // item is optional
173
- [label, item] = lArgs;
174
- assert(isString(label), `debug(): 1st arg ${OL(label)} should be a string`);
175
- if (doDebugDebug) {
176
- if (nArgs === 1) {
177
- LOG(`debug('${escapeStr(label)}') - 1 arg`);
178
- } else {
179
- LOG(`debug('${escapeStr(label)}', ${typeof item}) - 2 args`);
180
- }
181
- LOG(`debugging flag = ${OL(debugging)}`);
182
- }
183
- // --- We always need to manipulate the stack when we encounter
184
- // either "enter X" or "return from X", so we can't short-circuit
185
- // when debugging is off
186
- lResult = adjustStack(label);
287
+ // --- export only to allow unit tests
288
+ export var funcMatch = function(stack, lFuncList) {
289
+ var curFunc, h, j, len1, name, object, plus;
290
+ assert(isArray(lFuncList), `not an array ${OL(lFuncList)}`);
291
+ curFunc = stack.curFunc();
187
292
  if (doDebugDebug) {
188
- LOG('lResult', lResult);
293
+ LOG(`funcMatch(): curFunc = ${OL(curFunc)}`);
294
+ stack.dump(' ');
295
+ LOG('lFuncList', lFuncList);
189
296
  }
190
- [mainPre, auxPre, hEnv, type] = lResult;
191
- if (doDebugDebug && (type === undef)) {
192
- LOG("type is undef - NOT LOGGING");
193
- }
194
- hOptions = {
195
- prefix: mainPre,
196
- itemPrefix: auxPre
197
- };
198
- switch (type) {
199
- case 'enter':
200
- log(label, hOptions);
201
- if (nArgs === 2) {
202
- // --- don't repeat the label
203
- logItem(undef, item, hOptions);
297
+ for (j = 0, len1 = lFuncList.length; j < len1; j++) {
298
+ h = lFuncList[j];
299
+ ({name, object, plus} = h);
300
+ if (name === curFunc) {
301
+ if (doDebugDebug) {
302
+ LOG(" curFunc in lFuncList - match successful");
204
303
  }
205
- break;
206
- case 'return':
207
- log(label, hOptions);
208
- if (nArgs === 2) {
209
- // --- don't repeat the label
210
- logItem(undef, item, hOptions);
211
- }
212
- break;
213
- case 'string':
214
- if (nArgs === 2) {
215
- logItem(label, item, hOptions);
216
- } else {
217
- log(label, hOptions);
304
+ return true;
305
+ }
306
+ if (plus && stack.isActive(name)) {
307
+ if (doDebugDebug) {
308
+ LOG(` func ${OL(name)} is active - match successful`);
218
309
  }
219
- }
220
- if (hEnv) {
221
- orgDebugging = debugging;
222
- ({debugging, shouldLogFunc, shouldLogString} = hEnv);
223
- if (doDebugDebug) {
224
- trans = `${orgDebugging} => ${debugging}`;
225
- LOG(` Restore hEnv: debugging: ${trans}`);
310
+ return true;
226
311
  }
227
312
  }
228
- return true; // allow use in boolean expressions
313
+ if (doDebugDebug) {
314
+ LOG(" - no match");
315
+ }
316
+ return false;
229
317
  };
230
318
 
319
+ // ---------------------------------------------------------------------------
320
+ // --- type is one of: 'enter', 'return', 'string', 'object'
321
+ export var getType = function(str, nObjects) {
322
+ var lMatches;
323
+ if (lMatches = str.match(/^\s*enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/)) {
324
+ // --- We are entering function curFunc
325
+ return ['enter', lMatches[1]];
326
+ } else if (lMatches = str.match(/^\s*return.+from\s+([A-Za-z_][A-Za-z0-9_\.]*)/)) {
327
+ return ['return', lMatches[1]];
328
+ } else if (nObjects > 0) {
329
+ return ['objects', undef];
330
+ } else {
331
+ return ['string', undef];
332
+ }
333
+ };
231
334
 
232
335
  // ---------------------------------------------------------------------------
233
336
  reMethod = /^([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/;
234
337
 
235
338
  // ---------------------------------------------------------------------------
236
339
  export var checkTrace = function(block) {
237
- var funcName, i, lMatches, lStack, len, len1, line, ref;
340
+ var funcName, j, lMatches, lStack, len, len1, line, ref;
238
341
  // --- export only to allow unit tests
239
342
  lStack = [];
240
343
  ref = blockToArray(block);
241
- for (i = 0, len1 = ref.length; i < len1; i++) {
242
- line = ref[i];
344
+ for (j = 0, len1 = ref.length; j < len1; j++) {
345
+ line = ref[j];
243
346
  if (lMatches = line.match(/enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/)) {
244
347
  funcName = lMatches[1];
245
348
  lStack.push(funcName);
@@ -4,15 +4,28 @@ import yaml from 'js-yaml'
4
4
 
5
5
  import {
6
6
  assert, undef, isNumber, isInteger, isString, isHash, isFunction,
7
- escapeStr, sep_eq, sep_dash, pass
7
+ escapeStr, sep_eq, sep_dash, pass, OL,
8
8
  } from '@jdeighan/coffee-utils'
9
9
  import {blockToArray} from '@jdeighan/coffee-utils/block'
10
- import {tabify, untabify, indentation} from '@jdeighan/coffee-utils/indent'
10
+ import {
11
+ tabify, untabify, indentation, indented,
12
+ } from '@jdeighan/coffee-utils/indent'
11
13
 
12
14
  # --- This logger only ever gets passed a single string argument
13
15
  putstr = undef
16
+ doDebugLog = false
14
17
 
15
18
  export stringify = undef
19
+ fourSpaces = ' '
20
+
21
+ # ---------------------------------------------------------------------------
22
+
23
+ export debugLog = (flag=true) ->
24
+
25
+ doDebugLog = flag
26
+ if doDebugLog
27
+ LOG "doDebugLog = #{flag}"
28
+ return
16
29
 
17
30
  # ---------------------------------------------------------------------------
18
31
  # This is useful for debugging
@@ -23,9 +36,9 @@ export LOG = (lArgs...) ->
23
36
  if lArgs.length > 1
24
37
  # --- There's both a label and an item
25
38
  if (item == undef)
26
- console.log "#{label}: UNDEFINED"
39
+ console.log "#{label} = undef"
27
40
  else if (item == null)
28
- console.log "#{label}: NULL"
41
+ console.log "#{label} = null"
29
42
  else
30
43
  console.log sep_dash
31
44
  console.log "#{label}:"
@@ -112,19 +125,6 @@ maxOneLine = 32
112
125
 
113
126
  # ---------------------------------------------------------------------------
114
127
 
115
- fixStr = (str) ->
116
-
117
- if !str
118
- return ''
119
-
120
- # --- If putstr is console.log, we'll convert TAB char to 3 spaces
121
- if putstr == console.log
122
- return untabify(str)
123
- else
124
- return str
125
-
126
- # ---------------------------------------------------------------------------
127
-
128
128
  export log = (str, hOptions={}) ->
129
129
  # --- valid options:
130
130
  # prefix
@@ -132,8 +132,11 @@ export log = (str, hOptions={}) ->
132
132
  assert isFunction(putstr), "putstr not properly set"
133
133
  assert isString(str), "log(): not a string"
134
134
  assert isHash(hOptions), "log(): arg 2 not a hash"
135
+ prefix = fixForTerminal(hOptions.prefix)
136
+
137
+ if doDebugLog
138
+ LOG "CALL log(#{OL(str)}), prefix = #{OL(prefix)}"
135
139
 
136
- prefix = fixStr(hOptions.prefix)
137
140
  putstr "#{prefix}#{str}"
138
141
  return true # to allow use in boolean expressions
139
142
 
@@ -141,15 +144,20 @@ export log = (str, hOptions={}) ->
141
144
 
142
145
  export logItem = (label, item, hOptions={}) ->
143
146
  # --- valid options:
144
- # prefix - not used
145
- # itemPrefix - always used
147
+ # prefix
146
148
 
147
149
  assert isFunction(putstr), "putstr not properly set"
148
150
  assert !label || isString(label), "label a non-string"
149
151
  assert isHash(hOptions), "arg 3 not a hash"
150
152
 
151
- label = fixStr(label)
152
- prefix = fixStr(hOptions.itemPrefix || hOptions.prefix)
153
+ label = fixForTerminal(label)
154
+ prefix = fixForTerminal(hOptions.prefix)
155
+ assert prefix.indexOf("\t") == -1, "prefix has TAB"
156
+
157
+ if doDebugLog
158
+ LOG "CALL logItem(#{OL(label)}, #{OL(item)})"
159
+ LOG "prefix = #{OL(prefix)}"
160
+
153
161
  labelStr = if label then "#{label} = " else ""
154
162
 
155
163
  if (item == undef)
@@ -162,21 +170,36 @@ export logItem = (label, item, hOptions={}) ->
162
170
  else
163
171
  if label
164
172
  putstr "#{prefix}#{label}:"
165
- putBlock item, prefix
173
+ putBlock item, prefix + fourSpaces
166
174
  else if isNumber(item)
167
175
  putstr "#{prefix}#{labelStr}#{item}"
168
176
  else
169
- putstr "#{prefix}#{sep_dash}"
170
177
  if label
171
178
  putstr "#{prefix}#{label}:"
172
179
  for str in blockToArray(stringify(item, true)) # escape special chars
173
- putstr "#{prefix}#{indentation(1)}#{fixStr(str)}"
174
- putstr "#{prefix}#{sep_dash}"
180
+ putstr "#{prefix + fourSpaces}#{fixForTerminal(str)}"
175
181
 
176
182
  return true
177
183
 
178
184
  # ---------------------------------------------------------------------------
179
185
 
186
+ export shortEnough = (label, value) ->
187
+
188
+ return (value == undef)
189
+
190
+ # ---------------------------------------------------------------------------
191
+ # --- needed because Windows Terminal handles TAB chars badly
192
+
193
+ fixForTerminal = (str) ->
194
+
195
+ if !str
196
+ return ''
197
+
198
+ # --- convert TAB char to 4 spaces
199
+ return str.replace(/\t/g, fourSpaces)
200
+
201
+ # ---------------------------------------------------------------------------
202
+
180
203
  putBlock = (item, prefix='') ->
181
204
 
182
205
  putstr "#{prefix}#{sep_eq}"