@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,7 +1,8 @@
1
1
  # debug_utils.coffee
2
2
 
3
3
  import {
4
- assert, undef, error, croak, warn, isString, isFunction, isBoolean,
4
+ assert, undef, error, croak, warn, defined,
5
+ isString, isFunction, isBoolean, sep_dash,
5
6
  OL, escapeStr, isNumber, isArray, words, pass,
6
7
  } from '@jdeighan/coffee-utils'
7
8
  import {blockToArray} from '@jdeighan/coffee-utils/block'
@@ -9,17 +10,134 @@ import {untabify} from '@jdeighan/coffee-utils/indent'
9
10
  import {slurp} from '@jdeighan/coffee-utils/fs'
10
11
  import {CallStack} from '@jdeighan/coffee-utils/stack'
11
12
  import {
12
- log, logItem, LOG, setStringifier, orderedStringify,
13
+ getPrefix, addArrow, removeLastVbar,
14
+ } from '@jdeighan/coffee-utils/arrow'
15
+ import {
16
+ log, logItem, LOG, shortEnough,
13
17
  } from '@jdeighan/coffee-utils/log'
14
18
 
15
- # --- These are saved/restored on the call stack
16
- export debugging = false
17
- shouldLogFunc = (func) -> debugging
18
- shouldLogString = (str) -> debugging
19
-
20
- stack = new CallStack()
19
+ callStack = new CallStack()
21
20
  doDebugDebug = false
22
21
 
22
+ export shouldLog = undef # set in resetDebugging() and setDebugging()
23
+ export lFuncList = []
24
+
25
+ # ---------------------------------------------------------------------------
26
+
27
+ export debug = (label, lObjects...) ->
28
+
29
+ assert isString(label), "1st arg #{OL(label)} should be a string"
30
+
31
+ # --- We want to allow objects to be undef. Therefore, we need to
32
+ # distinguish between 1 arg sent vs. 2 or more args sent
33
+ nObjects = lObjects.length
34
+
35
+ # --- funcName is only set for types 'enter' and 'return'
36
+ [type, funcName] = getType(label, nObjects)
37
+ if doDebugDebug
38
+ LOG "debug(): type = #{OL(type)}"
39
+ LOG "debug(): funcName = #{OL(funcName)}"
40
+
41
+ switch type
42
+ when 'enter'
43
+ callStack.enter funcName
44
+ label = shouldLog(label, type, funcName, callStack)
45
+ when 'return'
46
+ label = shouldLog(label, type, funcName, callStack)
47
+ when 'string'
48
+ label = shouldLog(label, type, undef, callStack)
49
+ assert (nObjects == 0),
50
+ "multiple objects only not allowed for #{OL(type)}"
51
+ when 'objects'
52
+ label = shouldLog(label, type, undef, callStack)
53
+ assert (nObjects > 0),
54
+ "multiple objects only not allowed for #{OL(type)}"
55
+ doLog = defined(label)
56
+
57
+ if doDebugDebug
58
+ if nObjects == 0
59
+ LOG "debug(#{OL(label)}) - 1 arg"
60
+ else
61
+ LOG "debug(#{OL(label)}), #{nObjects} args"
62
+ LOG "doLog = #{OL(doLog)}"
63
+
64
+ if doLog
65
+ level = callStack.getLevel()
66
+ prefix = getPrefix(level)
67
+
68
+ if doDebugDebug
69
+ LOG "callStack", callStack
70
+ LOG "level = #{OL(level)}"
71
+ LOG "prefix = #{OL(prefix)}"
72
+
73
+ switch type
74
+ when 'enter'
75
+ log label, {prefix}
76
+ for obj,i in lObjects
77
+ if (i > 0)
78
+ log sep_dash, {prefix: removeLastVbar(prefix)}
79
+ logItem undef, obj, {prefix: removeLastVbar(prefix)}
80
+ when 'return'
81
+ log label, {prefix: addArrow(prefix)}
82
+ for obj,i in lObjects
83
+ if (i > 0)
84
+ log sep_dash, {prefix: removeLastVbar(prefix)}
85
+ logItem undef, obj, {prefix: removeLastVbar(prefix)}
86
+ when 'string'
87
+ log label, {prefix}
88
+ when 'objects'
89
+ if (nObjects==1) && shortEnough(label, lObjects[0])
90
+ logItem label, lObjects[0], {prefix}
91
+ else
92
+ if (label.indexOf(':') != label.length - 1)
93
+ label += ':'
94
+ log label, {prefix}
95
+ for obj in lObjects
96
+ logItem undef, obj, {prefix}
97
+
98
+ if (type == 'enter') && doLog
99
+ callStack.logCurFunc()
100
+ else if (type == 'return')
101
+ callStack.returnFrom funcName
102
+
103
+ return true # allow use in boolean expressions
104
+
105
+ # ---------------------------------------------------------------------------
106
+
107
+ export stdShouldLog = (label, type, funcName, stack) ->
108
+ # --- if type is 'enter', then funcName won't be on the stack yet
109
+ # returns the (possibly modified) label to log
110
+
111
+ # --- If we're logging now,
112
+ # but we won't be logging when funcName is activated
113
+ # then change 'enter' to 'call'
114
+
115
+ assert isString(label), "label #{OL(label)} not a string"
116
+ assert isString(type), "type #{OL(type)} not a string"
117
+ if (type == 'enter') || (type == 'return')
118
+ assert isString(funcName), "func name #{OL(funcName)} not a string"
119
+ else
120
+ assert funcName == undef, "func name #{OL(funcName)} not undef"
121
+ assert stack instanceof CallStack, "not a call stack object"
122
+ if doDebugDebug
123
+ LOG "stdShouldLog(#{OL(label)}, #{OL(type)}, #{OL(funcName)}, stack)"
124
+ LOG "stack", stack
125
+ LOG "lFuncList", lFuncList
126
+ switch type
127
+ when 'enter'
128
+ if funcMatch(stack, lFuncList)
129
+ return label
130
+
131
+ # --- As a special case, if we enter a function where we will not
132
+ # be logging, but we were logging in the calling function,
133
+ # we'll log out the call itself
134
+ else if stack.isLoggingPrev()
135
+ return label.replace('enter', 'call')
136
+ else
137
+ if funcMatch(stack, lFuncList)
138
+ return label
139
+ return undef
140
+
23
141
  # ---------------------------------------------------------------------------
24
142
 
25
143
  export debugDebug = (flag=true) ->
@@ -33,176 +151,124 @@ export debugDebug = (flag=true) ->
33
151
 
34
152
  resetDebugging = () ->
35
153
 
36
- debugging = false
37
154
  if doDebugDebug
38
- LOG "resetDebugging() - debugging = false"
39
- stack.reset()
40
- shouldLogFunc = (func) -> debugging
41
- shouldLogString = (str) -> debugging
155
+ LOG "resetDebugging()"
156
+ callStack.reset()
157
+ shouldLog = (label, type, funcName, stack) -> undef
42
158
  return
43
159
 
44
160
  # ---------------------------------------------------------------------------
45
161
 
46
- export setDebugging = (funcDoDebug=undef, funcDoLog=undef) ->
162
+ export setDebugging = (option) ->
47
163
 
48
164
  resetDebugging()
49
- if isBoolean(funcDoDebug)
50
- debugging = funcDoDebug
165
+ if isBoolean(option)
166
+ if option
167
+ shouldLog = (label, type, funcName, stack) -> label
168
+ else
169
+ shouldLog = (label, type, funcName, stack) -> undef
51
170
  if doDebugDebug
52
- LOG "setDebugging(): debugging = #{funcDoDebug}"
53
- else if isString(funcDoDebug)
54
- debugging = false
55
- lFuncNames = words(funcDoDebug)
56
- assert isArray(lFuncNames), "words('#{funcDoDebug}') returned non-array"
57
- shouldLogFunc = (funcName) ->
58
- funcMatch(funcName, lFuncNames)
171
+ LOG "setDebugging = #{option}"
172
+ else if isString(option)
173
+ lFuncList = getFuncList(option)
174
+ shouldLog = stdShouldLog
59
175
  if doDebugDebug
60
- LOG "setDebugging FUNCS: #{lFuncNames.join(',')}, debugging = false"
61
- else if isFunction(funcDoDebug)
62
- shouldLogFunc = funcDoDebug
176
+ LOG "setDebugging FUNCS: #{option}"
177
+ LOG 'lFuncList', lFuncList
178
+ else if isFunction(option)
179
+ shouldLog = option
63
180
  if doDebugDebug
64
181
  LOG "setDebugging to custom func"
65
182
  else
66
- croak "setDebugging(): bad parameter #{OL(funcDoDebug)}"
67
-
68
- if funcDoLog
69
- assert isFunction(funcDoLog), "setDebugging: arg 2 not a function"
70
- shouldLogString = funcDoLog
183
+ croak "bad parameter #{OL(option)}"
71
184
  return
72
185
 
73
186
  # ---------------------------------------------------------------------------
74
187
  # --- export only to allow unit tests
75
188
 
76
- export funcMatch = (curFunc, lFuncNames) ->
189
+ export getFuncList = (str) ->
190
+
191
+ lFuncList = []
192
+ for word in words(str)
193
+ if lMatches = word.match(///^
194
+ ([A-Za-z_][A-Za-z0-9_]*)
195
+ (?:
196
+ \.
197
+ ([A-Za-z_][A-Za-z0-9_]*)
198
+ )?
199
+ (\+)?
200
+ $///)
201
+ [_, ident1, ident2, plus] = lMatches
202
+ if ident2
203
+ lFuncList.push {
204
+ name: ident2
205
+ object: ident1
206
+ plus: (plus == '+')
207
+ }
208
+ else
209
+ lFuncList.push {
210
+ name: ident1
211
+ plus: (plus == '+')
212
+ }
213
+ else
214
+ croak "Bad word in func list: #{OL(word)}"
215
+ return lFuncList
77
216
 
78
- assert isString(curFunc), "funcMatch(): not a string"
79
- assert isArray(lFuncNames), "funcMatch(): bad array #{lFuncNames}"
80
- if lFuncNames.includes(curFunc)
81
- return true
82
- else if (lMatches = curFunc.match(reMethod)) \
83
- && ([_, cls, meth] = lMatches) \
84
- && lFuncNames.includes(meth)
85
- return true
86
- else
87
- return false
217
+ # ---------------------------------------------------------------------------
218
+ # --- export only to allow unit tests
219
+
220
+ export funcMatch = (stack, lFuncList) ->
221
+
222
+ assert isArray(lFuncList), "not an array #{OL(lFuncList)}"
223
+
224
+ curFunc = stack.curFunc()
225
+ if doDebugDebug
226
+ LOG "funcMatch(): curFunc = #{OL(curFunc)}"
227
+ stack.dump(' ')
228
+ LOG 'lFuncList', lFuncList
229
+ for h in lFuncList
230
+ {name, object, plus} = h
231
+ if (name == curFunc)
232
+ if doDebugDebug
233
+ LOG " curFunc in lFuncList - match successful"
234
+ return true
235
+ if plus && stack.isActive(name)
236
+ if doDebugDebug
237
+ LOG " func #{OL(name)} is active - match successful"
238
+ return true
239
+ if doDebugDebug
240
+ LOG " - no match"
241
+ return false
88
242
 
89
243
  # ---------------------------------------------------------------------------
90
- # 1. adjust call stack on 'enter' or 'return from'
91
- # 2. adjust debugging flag
92
- # 3. return [mainPrefix, auxPrefix, hEnv] - hEnv can be undef
93
- # 4. disable logging by setting mainPrefix to undef
244
+ # --- type is one of: 'enter', 'return', 'string', 'object'
94
245
 
95
- adjustStack = (str) ->
246
+ export getType = (str, nObjects) ->
96
247
 
97
- if (lMatches = str.match(///^
248
+ if lMatches = str.match(///^
98
249
  \s*
99
250
  enter
100
251
  \s+
101
252
  ([A-Za-z_][A-Za-z0-9_\.]*)
102
- ///))
103
- curFunc = lMatches[1]
104
- hEnv = {
105
- debugging
106
- shouldLogFunc
107
- shouldLogString
108
- }
109
- debugging = shouldLogFunc(curFunc)
110
- if doDebugDebug
111
- trans = "#{hEnv.debugging} => #{debugging}"
112
- LOG " ENTER #{curFunc}, debugging: #{trans}"
113
- [mainPre, auxPre, _] = stack.call(curFunc, hEnv, debugging)
114
- return [
115
- mainPre
116
- auxPre
117
- undef
118
- if shouldLogFunc(curFunc) then 'enter' else undef
119
- ]
120
- else if (lMatches = str.match(///^
253
+ ///)
254
+
255
+ # --- We are entering function curFunc
256
+ return ['enter', lMatches[1]]
257
+
258
+ else if lMatches = str.match(///^
121
259
  \s*
122
260
  return
123
261
  .+
124
262
  from
125
263
  \s+
126
264
  ([A-Za-z_][A-Za-z0-9_\.]*)
127
- ///))
128
- curFunc = lMatches[1]
129
- [mainPre, auxPre, hEnv] = stack.returnFrom(curFunc)
130
- if doDebugDebug
131
- LOG " RETURN FROM #{curFunc}"
132
- return [
133
- mainPre
134
- auxPre
135
- hEnv
136
- if shouldLogFunc(curFunc) then 'return' else undef
137
- ]
138
- else
139
- [mainPre, auxPre, _] = stack.logStr()
140
- return [
141
- mainPre
142
- auxPre
143
- undef
144
- if shouldLogString(str) then 'string' else undef
145
- ]
146
-
147
- # ---------------------------------------------------------------------------
148
-
149
- export debug = (lArgs...) ->
265
+ ///)
266
+ return ['return', lMatches[1]]
150
267
 
151
- # --- We want to allow item to be undef. Therefore, we need to
152
- # distinguish between 1 arg sent vs. 2 args sent
153
- nArgs = lArgs.length
154
- assert (nArgs==1) || (nArgs==2), "debug(): #{nArgs} args"
155
- [label, item] = lArgs
156
- assert isString(label),
157
- "debug(): 1st arg #{OL(label)} should be a string"
158
-
159
- if doDebugDebug
160
- if nArgs==1
161
- LOG "debug('#{escapeStr(label)}') - 1 arg"
162
- else
163
- LOG "debug('#{escapeStr(label)}', #{typeof item}) - 2 args"
164
- LOG "debugging flag = #{OL(debugging)}"
165
-
166
- # --- We always need to manipulate the stack when we encounter
167
- # either "enter X" or "return from X", so we can't short-circuit
168
- # when debugging is off
169
-
170
- lResult = adjustStack(label)
171
- if doDebugDebug
172
- LOG 'lResult', lResult
173
- [mainPre, auxPre, hEnv, type] = lResult
174
- if doDebugDebug && (type == undef)
175
- LOG "type is undef - NOT LOGGING"
176
-
177
- hOptions = {
178
- prefix: mainPre
179
- itemPrefix: auxPre
180
- }
181
-
182
- switch type
183
- when 'enter'
184
- log label, hOptions
185
- if (nArgs==2)
186
- # --- don't repeat the label
187
- logItem undef, item, hOptions
188
- when 'return'
189
- log label, hOptions
190
- if (nArgs==2)
191
- # --- don't repeat the label
192
- logItem undef, item, hOptions
193
- when 'string'
194
- if (nArgs==2)
195
- logItem label, item, hOptions
196
- else
197
- log label, hOptions
198
-
199
- if hEnv
200
- orgDebugging = debugging
201
- {debugging, shouldLogFunc, shouldLogString} = hEnv
202
- if doDebugDebug
203
- trans = "#{orgDebugging} => #{debugging}"
204
- LOG " Restore hEnv: debugging: #{trans}"
205
- return true # allow use in boolean expressions
268
+ else if (nObjects > 0)
269
+ return ['objects', undef]
270
+ else
271
+ return ['string', undef]
206
272
 
207
273
  # ---------------------------------------------------------------------------
208
274