@jdeighan/coffee-utils 7.0.49 → 7.0.52

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,
@@ -8,9 +8,11 @@ import {
8
8
  error,
9
9
  croak,
10
10
  warn,
11
+ defined,
11
12
  isString,
12
13
  isFunction,
13
14
  isBoolean,
15
+ sep_dash,
14
16
  OL,
15
17
  escapeStr,
16
18
  isNumber,
@@ -35,28 +37,170 @@ import {
35
37
  CallStack
36
38
  } from '@jdeighan/coffee-utils/stack';
37
39
 
40
+ import {
41
+ getPrefix,
42
+ addArrow,
43
+ removeLastVbar
44
+ } from '@jdeighan/coffee-utils/arrow';
45
+
38
46
  import {
39
47
  log,
40
48
  logItem,
41
49
  LOG,
42
- setStringifier,
43
- orderedStringify
50
+ shortEnough
44
51
  } from '@jdeighan/coffee-utils/log';
45
52
 
46
- // --- These are saved/restored on the call stack
47
- export var debugging = false;
53
+ callStack = new CallStack();
48
54
 
49
- shouldLogFunc = function(func) {
50
- return debugging;
51
- };
55
+ doDebugDebug = false;
56
+
57
+ export var shouldLog = undef; // set in resetDebugging() and setDebugging()
58
+
59
+ export var lFuncList = [];
52
60
 
53
- shouldLogString = function(str) {
54
- 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
55
162
  };
56
163
 
57
- stack = new CallStack();
58
164
 
59
- 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
+ };
60
204
 
61
205
  // ---------------------------------------------------------------------------
62
206
  export var debugDebug = function(flag = true) {
@@ -68,174 +212,137 @@ export var debugDebug = function(flag = true) {
68
212
 
69
213
  // ---------------------------------------------------------------------------
70
214
  resetDebugging = function() {
71
- debugging = false;
72
215
  if (doDebugDebug) {
73
- LOG("resetDebugging() - debugging = false");
216
+ LOG("resetDebugging()");
74
217
  }
75
- stack.reset();
76
- shouldLogFunc = function(func) {
77
- return debugging;
78
- };
79
- shouldLogString = function(str) {
80
- return debugging;
218
+ callStack.reset();
219
+ shouldLog = function(label, type, funcName, stack) {
220
+ return undef;
81
221
  };
82
222
  };
83
223
 
84
224
  // ---------------------------------------------------------------------------
85
- export var setDebugging = function(funcDoDebug = undef, funcDoLog = undef) {
86
- var lFuncNames;
225
+ export var setDebugging = function(option) {
87
226
  resetDebugging();
88
- if (isBoolean(funcDoDebug)) {
89
- 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
+ }
90
237
  if (doDebugDebug) {
91
- LOG(`setDebugging(): debugging = ${funcDoDebug}`);
238
+ LOG(`setDebugging = ${option}`);
92
239
  }
93
- } else if (isString(funcDoDebug)) {
94
- debugging = false;
95
- lFuncNames = words(funcDoDebug);
96
- assert(isArray(lFuncNames), `words('${funcDoDebug}') returned non-array`);
97
- shouldLogFunc = function(funcName) {
98
- return funcMatch(funcName, lFuncNames);
99
- };
240
+ } else if (isString(option)) {
241
+ lFuncList = getFuncList(option);
242
+ shouldLog = stdShouldLog;
100
243
  if (doDebugDebug) {
101
- LOG(`setDebugging FUNCS: ${lFuncNames.join(',')}, debugging = false`);
244
+ LOG(`setDebugging FUNCS: ${option}`);
245
+ LOG('lFuncList', lFuncList);
102
246
  }
103
- } else if (isFunction(funcDoDebug)) {
104
- shouldLogFunc = funcDoDebug;
247
+ } else if (isFunction(option)) {
248
+ shouldLog = option;
105
249
  if (doDebugDebug) {
106
250
  LOG("setDebugging to custom func");
107
251
  }
108
252
  } else {
109
- croak(`setDebugging(): bad parameter ${OL(funcDoDebug)}`);
110
- }
111
- if (funcDoLog) {
112
- assert(isFunction(funcDoLog), "setDebugging: arg 2 not a function");
113
- shouldLogString = funcDoLog;
253
+ croak(`bad parameter ${OL(option)}`);
114
254
  }
115
255
  };
116
256
 
117
257
  // ---------------------------------------------------------------------------
118
258
  // --- export only to allow unit tests
119
- export var funcMatch = function(curFunc, lFuncNames) {
120
- var _, cls, lMatches, meth;
121
- assert(isString(curFunc), "funcMatch(): not a string");
122
- assert(isArray(lFuncNames), `funcMatch(): bad array ${lFuncNames}`);
123
- if (lFuncNames.includes(curFunc)) {
124
- return true;
125
- } else if ((lMatches = curFunc.match(reMethod)) && ([_, cls, meth] = lMatches) && lFuncNames.includes(meth)) {
126
- return true;
127
- } else {
128
- return false;
129
- }
130
- };
131
-
132
- // ---------------------------------------------------------------------------
133
- // 1. adjust call stack on 'enter' or 'return from'
134
- // 2. adjust debugging flag
135
- // 3. return [mainPrefix, auxPrefix, hEnv] - hEnv can be undef
136
- // 4. disable logging by setting mainPrefix to undef
137
- adjustStack = function(str) {
138
- var _, auxPre, curFunc, hEnv, lMatches, mainPre, trans;
139
- if ((lMatches = str.match(/^\s*enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/))) {
140
- curFunc = lMatches[1];
141
- hEnv = {debugging, shouldLogFunc, shouldLogString};
142
- debugging = shouldLogFunc(curFunc);
143
- if (doDebugDebug) {
144
- trans = `${hEnv.debugging} => ${debugging}`;
145
- LOG(` ENTER ${curFunc}, debugging: ${trans}`);
146
- }
147
- [mainPre, auxPre, _] = stack.call(curFunc, hEnv, debugging);
148
- return [mainPre, auxPre, undef, shouldLogFunc(curFunc) ? 'enter' : undef];
149
- } else if ((lMatches = str.match(/^\s*return.+from\s+([A-Za-z_][A-Za-z0-9_\.]*)/))) {
150
- curFunc = lMatches[1];
151
- [mainPre, auxPre, hEnv] = stack.returnFrom(curFunc);
152
- if (doDebugDebug) {
153
- 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)}`);
154
281
  }
155
- return [mainPre, auxPre, hEnv, shouldLogFunc(curFunc) ? 'return' : undef];
156
- } else {
157
- [mainPre, auxPre, _] = stack.logStr();
158
- return [mainPre, auxPre, undef, shouldLogString(str) ? 'string' : undef];
159
282
  }
283
+ return lFuncList;
160
284
  };
161
285
 
162
286
  // ---------------------------------------------------------------------------
163
- export var debug = function(...lArgs) {
164
- var auxPre, hEnv, hOptions, item, lResult, label, mainPre, nArgs, orgDebugging, trans, type;
165
- // --- We want to allow item to be undef. Therefore, we need to
166
- // distinguish between 1 arg sent vs. 2 args sent
167
- nArgs = lArgs.length;
168
- assert((nArgs === 1) || (nArgs === 2), `debug(): ${nArgs} args`);
169
- [label, item] = lArgs;
170
- assert(isString(label), `debug(): 1st arg ${OL(label)} should be a string`);
171
- if (doDebugDebug) {
172
- if (nArgs === 1) {
173
- LOG(`debug('${escapeStr(label)}') - 1 arg`);
174
- } else {
175
- LOG(`debug('${escapeStr(label)}', ${typeof item}) - 2 args`);
176
- }
177
- LOG(`debugging flag = ${OL(debugging)}`);
178
- }
179
- // --- We always need to manipulate the stack when we encounter
180
- // either "enter X" or "return from X", so we can't short-circuit
181
- // when debugging is off
182
- 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();
183
292
  if (doDebugDebug) {
184
- LOG('lResult', lResult);
293
+ LOG(`funcMatch(): curFunc = ${OL(curFunc)}`);
294
+ stack.dump(' ');
295
+ LOG('lFuncList', lFuncList);
185
296
  }
186
- [mainPre, auxPre, hEnv, type] = lResult;
187
- if (doDebugDebug && (type === undef)) {
188
- LOG("type is undef - NOT LOGGING");
189
- }
190
- hOptions = {
191
- prefix: mainPre,
192
- itemPrefix: auxPre
193
- };
194
- switch (type) {
195
- case 'enter':
196
- log(label, hOptions);
197
- if (nArgs === 2) {
198
- // --- don't repeat the label
199
- logItem(undef, item, hOptions);
200
- }
201
- break;
202
- case 'return':
203
- log(label, hOptions);
204
- if (nArgs === 2) {
205
- // --- don't repeat the label
206
- 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");
207
303
  }
208
- break;
209
- case 'string':
210
- if (nArgs === 2) {
211
- logItem(label, item, hOptions);
212
- } else {
213
- 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`);
214
309
  }
215
- }
216
- if (hEnv) {
217
- orgDebugging = debugging;
218
- ({debugging, shouldLogFunc, shouldLogString} = hEnv);
219
- if (doDebugDebug) {
220
- trans = `${orgDebugging} => ${debugging}`;
221
- LOG(` Restore hEnv: debugging: ${trans}`);
310
+ return true;
222
311
  }
223
312
  }
224
- return true; // allow use in boolean expressions
313
+ if (doDebugDebug) {
314
+ LOG(" - no match");
315
+ }
316
+ return false;
225
317
  };
226
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
+ };
227
334
 
228
335
  // ---------------------------------------------------------------------------
229
336
  reMethod = /^([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/;
230
337
 
231
338
  // ---------------------------------------------------------------------------
232
339
  export var checkTrace = function(block) {
233
- var funcName, i, lMatches, lStack, len, len1, line, ref;
340
+ var funcName, j, lMatches, lStack, len, len1, line, ref;
234
341
  // --- export only to allow unit tests
235
342
  lStack = [];
236
343
  ref = blockToArray(block);
237
- for (i = 0, len1 = ref.length; i < len1; i++) {
238
- line = ref[i];
344
+ for (j = 0, len1 = ref.length; j < len1; j++) {
345
+ line = ref[j];
239
346
  if (lMatches = line.match(/enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/)) {
240
347
  funcName = lMatches[1];
241
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}"