Question
· Dec 14, 2022

How to get current routine line?

I am familiar with $TEXT which can get you any line in the current routine provided you know the offset. For example, $T(+1) will get you the first line of the current routine at the run time. In the same vein, how do I reference the current line number/offset at the run time? Something like $T(+$CURRENTLINENUMBER) where $CURRENTLINENUMBER is not yet known to me function. The sample below would write 3 as the line number.

RTNNAME
 S A=1
W $CURRENTLINENUMBER

Product version: Caché 2017.1
Discussion (13)4
Log in or sign up to continue

Hi Anna. I have written  a function to work for MAC, INT and INT generated from Classes, based on stack comment earlier. For user to test and confirm suitable for purpose :)

Usage from a class method or routine. Returns the line number. Arguments optional to capture other context.  

// how to call
set linenumber= ##class(DC.LineNumber).SrcLineNumberFromStack(.routine,.label,.offset,.src)
// debug
write !,"routine: ",routine
write !,"label: ",label
write !,"offset: ",offset
write !,"linenumber: ",linenumber
write !,"src:",src

Implementation:

Class DC.LineNumber [ Abstract ]
{ 
ClassMethod SrcLineNumber(routine As %String, label As %String, offset As %Integer = 0, Output src) As %Integer
{
if '$Data(^rMAC(routine)),$Data(^ROUTINE(routine)) {
// INT code only possibly generated
set globref="^ROUTINE" 
else {
set globref="^rMAC" 
}
set labelLen=$Length(label)
set line=0
for {
// Try use rMAC if available as ROUTINE may have subsequent imported includes changing original line offsets
set line=$Order(@globref@(routine,0,line),1,data)
// reached the end of the routine
quit:line=""
set matchTest=$Extract(data,1,labelLen)
// discard non-matching lines
continue:$Extract(data,1,labelLen)'=label
// Looking for an exact match eg: Myline
// skip linelabels with prefix same as exact line label
// eg: MyLableBefore, MyLableAfter
// ie comprised by following alphanumeric character 
continue:$Extract(data,labelLen+1)?1AN
// Found the line so quit here
quit:matchTest=label
}
set line=line+offset
set src=$Select(line>0:$Get(@globref@(routine,0,line)),1:"")
quit line
}
/// Unpack current location
ClassMethod SrcLineNumberFromStack(Output routine As %String, Output label As %Integer, Output offset As %Integer, Output src As %String) As %Integer
{
quit:$Stack=1 0
set place=$Stack(($Stack)-1,"PLACE")
set routine=$Piece($Piece(place,"^",2)," ")
set offset=+$P($Piece(place,"^"),"+",2)
set label=$P($Piece(place,"^"),"+")
set line=(..SrcLineNumber(routine,label,offset,.src))
quit line
}

Things are not so easy as they seem, you have to consider scopes too. Take the above class (DC.LineNumber) and add three more methods:

ClassMethod CaseA(x)
{
    if x goto zTest
    quit "A0"
    
zTest quit "A1"
}

ClassMethod CaseB(x)
{
    if x goto Test
    quit "B0"
    
Test quit "B1"
}

ClassMethod Test()
{
    write ..CaseA(0),..CaseA(1) set linenumber=..SrcLineNumberFromStack(.routine,.label,.offset,.src) do prt
    write ..CaseB(1),..CaseB(0) set linenumber=..SrcLineNumberFromStack(.routine,.label,.offset,.src) do prt
    quit
    
    // debug
prt	write !,"routine: ",routine
    write !,"label: ",label
    write !,"offset: ",offset
    write !,"linenumber: ",linenumber
    write !,"src:",src,!!
}

and now do the test:

do ##class(DC.LineNumber).Test()

and check the output... 

OK, I know, this is a (very) constructed case and shouldn't coincide with an everyday development style, but who knows, what a mad programer sometimes produces...