Question
· Aug 23, 2020

Function to list all written function in a Cache Routine

Hi Everyone,

Has anyone tried to write down a function in a routine that list all of possible function inside the routine?
I can only think of iterating through the routine line one by one and differentiate between function and function definition.
Is there any existing function that can be utilised? I think it should work similar to how

do function^routineName 

works. I could not find the source for how "do" works, but I believe do will look at the list of function in the routine, and if it finds it, it will call the function, but if it does not find it, it will give an error?

Can someone please shed a light on this? Thank you.

EDIT:
My goal is to create a menu for a utility routine. This menu function should be able to detect all the function written in the routine automatically, so everytime a new function is added, calling the menu will show the new function I have written.

Manually, this is what I would do:
image
But this will require me to update the function every time.

Discussion (5)1
Log in or sign up to continue

With pure routines, this is an almost impossible attempt.
Since DO doesn't  have a differentiator between calling a function or procedure, internal or public or just looping  [ do {}  while ]

If you do it inside a class you have all this information on functions and procedures in %Dictionary.*
So you can define exactly what you search. And using the code generator you may even do it at compile time.

menu.mac

ROUTINE menu
menu(routine) {
  Set rtn = ##class(%RoutineMgr).%OpenId(routine_".mac")
  Quit:'$IsObject(rtn)
  Set menu = ""
  While 'rtn.Code.AtEnd {
    Set line = rtn.Code.ReadLine()
    Continue:$Char(9,32,35)[$Extract(line)
    Continue:line'[" ; "
    Set label = $Piece($Piece(line, " "), "(")
    Set title = $Piece(line, " ; ", 2, *)
    Set menu = menu _ $Listbuild($Listbuild(label, title))
  }
  Quit menu
}
asLine(menu, pos) public {
  Set menuItem = $Listget(menu, pos)
  Quit:menuItem="" """"""
  Set $Listbuild(label,title) = menuItem
  Quit "$Char(13,10)_""" _ $Justify(pos, 4) _ ". " _ title_ """"
}

menu.inc

ROUTINE menu [Type=INC]

#Execute Set menu = $$^menu(rtname)

  Write !,"--------Menu---------"
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
  write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))

  Write !!
  do {
    Write $Char(13),"Option? ",*27,"[0K"
    Read menuOption
    Quit:menuOption=""
    Quit:"qQ"[$Extract(menuOption)
    Quit:$Listget(menu,+menuOption)'=""
  } while 1
  Write !
  if (+menuOption) {
    Set label = $Listget($Listget(menu, menuOption))
    Do @label
  }
  Quit 

And some routine which will have to have menu

ROUTINE test
#; just include menu, at the place where you need it
#Include menu
  quit

task1 ; Task 1
  Write !,"Some work 1"
  quit
task2 ; Task 2
  Write !,"Some work 2"
  quit
task3 ; Task 3
  Write !,"Some work 3"
  quit
task4 ; Task 4
  Write !,"Some work 4"
  quit
task5 ; Task 5
  Write !,"Some work 5"
  quit
task6 ; Task 6
  Write !,"Some work 6"
  quit
task7 ; Task 7
  Write !,"Some work 7"
  quit
task8 ; Task 8
  Write !,"Some work 8"
  quit
task9 ; Task 9
  Write !,"Some work 9"
  quit
task10 ; Task 10
  Write !,"Some work 10"
  quit

and call it

USER>d ^test

--------Menu---------
   1. Task 1
   2. Task 2
   3. Task 3
   4. Task 4
   5. Task 5
   6. Task 6
   7. Task 7
   8. Task 8
   9. Task 9
  10. Task 10

Option? 1

Some work 1

Menu list changed after compile, but routine should be saved as MAC.
The final INT code contains the generated menu.

Nice done Dmitriy! 

I hardened your inc routine a bit so as not to change the variables in the mac routine:

ROUTINE menu [Type=INC]

 #Execute set menuLineNum=0
  do myMenu()
myMenu(){
    Set menu = $$^menu($zname)
    Write !,"--------Menu---------"
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    write ##Expression($$asLine^menu(menu,$Increment(menuLineNum)))
    Write !!
    do {
      Write $Char(13),"Option? ",*27,"[0K"
      Read menuOption
      Quit:menuOption=""
      Quit:"qQ"[$Extract(menuOption)
      Quit:$Listget(menu,+menuOption)'=""
    while 1
    Write !
    if (+menuOption) {
      Set label = $Listget($Listget(menu, menuOption))
      Do @label
    }
  }

Going back to good old fashioned MUMPs this works for INTs:

CALLS(rtn) ;
NSP,EXT,VER,ROU,LAST,LINE
  ; NSP = namespace
  ; EXT = extension/type of routine e.g. INT
  ; VER = version, default is zero
  ; ROU = routine name
; LAST = last line or routine i.e. number of lines
ROU=$g(rtn)
ROU="" 0 !,"No routine reference supplied" q
NSP="%SYS",EXT="INT",VER=0,LINE=""
  ROU=$g(rtn)_"."_EXT_"."_VER
  ; get last line
  LAST=$$LENGTH^%R(ROU,NSP)
  LAST>0 d
  . i=1:1:LAST d
  . . LINE=$$LINE^%R(ROU,i,NSP)
  . . ; check first character of line against ASCII value
  . . ; ignore anything below 48 which is zero
  . . $a($e(LINE,1))>47 0 !,i,?10,LINE
  q