module sic_interpreter
  use sic_structures
  !---------------------------------------------------------------------
  !  Support variables for the SIC interpreter. Variables local to this
  ! file only.
  !---------------------------------------------------------------------
  !
  logical :: freelet           ! Is a free syntax assignment suspected?
  logical :: sic_quiet_proced  !
  logical :: do_log            !
  integer :: ifllev(mcloop)    !
  integer :: lire              !
  integer :: liro              !
  integer(kind=4) :: nliro     !
  character(len=commandline_length) :: oline  !
  integer(kind=4) :: nolin                    !
  logical, save :: ignore_if = .false.  ! Default at startup
  logical, save :: lenter = .false.  ! Default at startup
  !
end module sic_interpreter
!
subroutine sic_run(line,lang,command,error,icode,ocode)
  use sic_dependencies_interfaces
  use gildas_def
  use sic_structures
  use sic_dictionaries
  use sic_interactions
  use sic_interpreter
  use sic_interfaces, except_this=>sic_run
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public (but private in the context of new language initialization)
  !       Read next command and interprets it, then return
  !       control to the caller.
  !
  ! Arguments:
  ! LINE:  Command line. On input: text to be stored in stack and log
  !        file. On output: new command line
  ! ERROR: Logical error flag, indicating whether last command was
  !        successfully executed.
  !
  ! Note on labels in the code
  !       10      Error handling after command execution
  !       20      Read new command
  !       30      Check for error in command reading and level changes
  !       40      Substitute symbols and analyse line
  !       50      Log file after command execution
  !       70      DO WHILE in QUIT ALL
  !       80      DO WHILE in RETURN BASE
  !       90      DO WHILE in EXIT
  !---------------------------------------------------------------------
  character(len=*), intent(inout) :: line     ! Input/output command line
  character(len=*), intent(out)   :: lang     ! Language found
  character(len=*), intent(out)   :: command  ! Command found
  logical,          intent(inout) :: error    ! Logical error flag
  integer,          intent(in)    :: icode    ! Input code
  integer,          intent(out)   :: ocode    ! Output code
  ! Local
  integer(kind=4) :: nline,ier,n,goto_code,recall,ntmp
  logical :: err,suite,nolog
  character(len=4) :: ans
  character(len=message_length) :: mess,tmp
  !
  ! Output code: get next command.
  ocode = code_next
  !
  ! SIC_ENTER
  if (icode.eq.code_enter) then
    !----------------------------------------------------------------------
    !       Enter SIC
    !----------------------------------------------------------------------
    if (.not.loaded) then
      call sic_message(seve%f,'SIC','SIC is not loaded')
      call sysexi(fatale)
    endif
    if (lenter) then
      call sic_message(seve%f,'SIC','Programming error: recursive call to SIC')
      call sysexi(fatale)
    endif
    nlire = 1
    nliro = 1
    mlire(nlire)=0
    lenter = .true.
    error = .false.
    goto 20
    !
    ! SIC_PLAY
  elseif (icode.eq.code_play) then
    !----------------------------------------------------------------------
    !       Enter SIC and Execute a first SIC command, which may be @.
    !----------------------------------------------------------------------
    if (.not.loaded) then
      call sic_message(seve%f,'SIC','SIC is not loaded')
      call sysexi(fatale)
    endif
    if (lenter) then
      call sic_message(seve%f,'SIC','Programming error: recursive call to SIC')
      call sysexi(fatale)
    endif
    lenter = .true.
    nlire = 1
    mlire(nlire) = 0
    error = .false.
    nline = lenc(line)
    suite = .false.
    call sblanc(line,nline,suite,ier)
    if (ier.ne.1) then
      error = .true.
    else
      call aroba(line,nline,error)
    endif
    goto 30
    !
    ! SIC_EXEC
  elseif (icode.eq.code_exec) then
    !----------------------------------------------------------------------
    !       Execute a single SIC command, which may be @.
    !       Upon completion return to the caller.
    !----------------------------------------------------------------------
    if (.not.loaded) then
      call sic_message(seve%f,'SIC','SIC is not loaded')
      call sysexi(fatale)
    endif
    !
    ! Increment execution depth
    nlire = nlire+1
    mlire(nlire) = -1
    lire = -1
    if_depth(nlire) = if_current+1
    error = .false.
    nline = lenc(line)
    suite = .false.
    call sblanc(line,nline,suite,ier)
    if (ier.ne.1) then
      error = .true.
    else
      call aroba(line,nline,error)
    endif
    goto 30
  endif
  !
  ! Restore interactive mode: moved to label 20...
  !      LIBRARY_MODE = .FALSE.
  !
  ! Store in log file
50 nline=lenc(line)
  if (nline.eq.0) goto 10
  if (.not.error .and. vocab(ccomm%icom)(1:1).ne.'*') then
    call sic_log (line,nline,lire)
    if (do_log) then
      if (nlire.ge.1 .and. liro.eq.0    .and.  &  ! LIRO here
          vocab(ccomm%icom)(1:1).ne.'#' .and.  &
          vocab(ccomm%icom)(1:1).ne.'$')  &
         call insert(oline,nolin)
      !  call insert(line,nline)
    endif
    do_log = logbuf
  endif
  !
  ! Handle errors and Control_C trapping
10 continue
  call ctrlc_check(error)
  !
  ! Get the original command line again
  line = oline
  nline = nolin
  !
  call erreur(error,line,nline,lire,err)
  if (err) then
    ! SYSTEM call or SIC call?
    if (sic_format_system_token(line,nline)) then
      goto 31
    else
      suite = .false.
      call sblanc(line,nline,suite,ier)
      if (ier.ne.1) then
        error = .true.
        goto 30                  ! Yes, not 40 in this case
      else
        call aroba(line,nline,error)
        goto 40
      endif
    endif
  endif
  !
  if (nlire.eq.0) then
    ! If NLIRE=0, exit from SIC. This happens in not-interactive mode, e.g.
    ! shell> sic < foo.sic
    call gmaster_on_exit(error)
    lenter = .false.
    ocode  = code_exit
    return
    !
  elseif (mlire(nlire).eq.-1) then
    ! Decrease the level and stop execution of the current one (code_end)
    nlire = nlire-1
    if (nlire.ne.0) then
      lire = mlire(nlire)
    else
      lire = 0
    endif
    call reset_if (err)
    ocode = code_end           ! End CODE_EXEC loop
    return
  endif
  !
  ! Read a new command
20 error = .false.
  library_mode = .false.       ! Restore interactive mode
  lire=mlire(nlire)
  !
  call bldprt(nlire)           ! Build prompt
  !D print *,'TEST Calling GETCOM ',error
  call getcom(line,nline,lire,gprompt,lgprompt,error)
  !D print *,'TEST GetCom error ',error,line(1:nline)
  ! Save command line
  !      OLINE = LINE(1:NLINE)
  !      NOLIN = NLINE
  !
  ! Command processing
30 continue
  if (error) then
    if (sic_quiet) goto 20
    goto 10
  endif
  !
  ! System call
31 continue
  !
  ! The $ is only allowed interactively. Use SYSTEM command otherwise
  if (line(1:1).eq.'$' .and. .not.compil) then
    if (mlire(nlire).eq.0) then
      ier = gag_system (line(2:nline))
      if (do_log) call insert (line,nline)
      goto 20
    else
      call sic_message(seve%e,'SIC','$ is only valid at the interactive prompt')
      call sic_translate_system_token(line,nline,tmp,ntmp)
      call sic_message(seve%e,'SIC','Use instead: '//tmp(1:ntmp))
      error = .true.
      ! Save line for proper error prompt
      oline = line(1:nline)
      nolin = nline
      goto 10
    endif
  endif
  !
  ! End of level execution
  if (nline.eq.0) then
    if (compil) then
      call sic_message(seve%e,'SIC','Incorrect nesting of loops and macros')
      error = .true.
      goto 10
    endif
    nlire=nlire-1
    call reset_if (error)
    if (error) then
      call sic_message(seve%e,'SIC','Incorrect nesting of IF blocks')
    endif
    if (lire.gt.0) then
      call endmac
    elseif (lire.eq.-1) then
      if (nlire.ne.0) then
        lire = mlire(nlire)
      else
        lire = 0
      endif
      ocode = code_end         ! End CODE_EXEC loop
      return
      !
      ! This can happen if an ENDIF command is missing...
    elseif (lire.eq.-2) then
      mess = 'Invalid nesting of loop # and IF blocks'
      mess(25:25) = char(cloop+ichar('0'))
      call sic_message(seve%e,'SIC',mess)
      call finloo
      error = .true.
    endif
    goto 10
  endif
  !
40 continue
  ! Save command line
  oline = line(1:nline)
  nolin = nline
  !
  ! Translate symbols
  call replace_symbol(line,nline,error)
  if (error) then
    if (sic_quiet) goto 20
    goto 10
  endif
  !
  ! Translate Logical Names
  call replace_logical(line,nline,error)
  if (error) then
    if (sic_quiet) goto 20
    goto 10
  endif
  !
  ! Check for alternate syntax A = Something
100 continue
  freelet = .false.
  if (lsynt.gt.1) then
    call setlet(line,nline,freelet,error)
    if (error) then
      if (sic_quiet) goto 20
      goto 10
    endif
  endif
  !
  ! Find the command
  if (line(1:1).eq.' ')  goto 10
  !
  if (line(1:nline).eq.'C '.or.line(1:nline).eq.'c ') then
    ! Single letter 'C' assumed to be unambiguous reference to 'CONTINUE'.
    line = 'CONTINUE'
    nline= 8
  endif
  !
  if (proced .or. compil) then
    continue  ! Do nothing at this stage
    !
  else
    !
    ! Debug macros if requested
    if (lire.gt.0) then
      if (sic_stepin.gt.0 .and. nline.gt.1) then
        if (.not.lverif) call sic_wprn(line(1:nline)//' ? ',ans,n)
      endif
    endif
    !
    ! Interpret
    call sic_find(command,line,nline,error)
    if (error) then
      if (sic_quiet) goto 20
      goto 10
    endif
    lang = ccomm%lang
    !
    ! Patch for user commands
    if (languages(ccomm%ilang)%user) then
      if (logbuf .and. nlire.ge.1 .and. lire.eq.0)  call insert(line,nline)
      do_log = .false.
      call replace_usercom(languages(ccomm%ilang),command,line,nline,error)
      ! Reprocess the command by GETCOM: it may contain several
      ! commands in one line:
      ! print *,'TEST PUTCOM ',Line(1:nline)
      call putcom(line,nline,error)
      ! print *,'TEST ',Line(1:nline)
      goto 100
    endif
  endif
  !
  liro=lire
  !
  ! Procedure definition
  if (proced) then
    call sic_proced(line,nline,error)
    goto 10
  endif
  !
  if (compil) then
    ! Compilation of loop
    call sic_compil(line,nline,goto_code,error)
    !
    select case (goto_code)
    case(20)
      goto 20
    case(40)
      goto 40
    case default
      goto 10
    end select
    !
  elseif (nlire.gt.0 .and. if_current.ge.if_depth(nlire)) then
    ! Exploration of an IF block
    call sic_ifblock(line,nline,command,goto_code,error)
    !
    select case (goto_code)
    case(10)
      goto 10
    case(20)
      goto 20
    case(50)
      goto 50
    end select
    ! Default => continue
  endif
  !
  ! Execution : User commands
  if (ccomm%icom.gt.mbuilt_sic_inter) return  ! Command is not a basic SIC\ one.
  ! This assumes that SIC\ is the first language loaded and that the
  ! commands below are the first in this language.
  call sic_run_comm(command,line,nline,ocode,recall,nolog,error)
  ! Take care of some commands which can return with ERROR true AND
  ! OCODE.NE.0  (QUIT [ALL], RETURN ERROR). OCODE has precedence in this
  ! case (we should not retrieve more command lines before leaving
  ! 'sic_run', else they could be ignored by caller).
  if (ocode.ne.0)  return
  if (error)  goto 10
  if (recall.eq.1)  goto 40  ! Sic call
  if (nolog) goto 10
  !
  ! Handle log file and stack
  goto 50
  !
end subroutine sic_run
!
subroutine sic_if(case)
  use sic_interpreter
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  logical, intent(in) :: case  !
  ignore_if = case
end subroutine sic_if
!
subroutine sic_run_comm(command,line,nline,ocode,recall,nolog,error)
  use sic_dependencies_interfaces
  use gbl_message
  use sic_interfaces, except_this=>sic_run_comm
  use sic_interactions
  use sic_interpreter
  use sic_structures
  !---------------------------------------------------------------------
  ! @ private
  ! Execute a basic SIC\ command. A "basic" SIC command is one of these
  ! SIC\ commands which is deeply bound with the SIC interpreter, i.e.
  ! which can not be executed out of it.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: command  ! Command
  character(len=*), intent(inout) :: line     ! Command line
  integer(kind=4),  intent(inout) :: nline    ! Length of command line
  integer(kind=4),  intent(out)   :: ocode    ! Output code
  integer(kind=4),  intent(out)   :: recall   ! Execute a new command?
  logical,          intent(out)   :: nolog    ! Log it or not on return?
  logical,          intent(inout) :: error    ! Logical error flag (intent(out)?)
  ! Local
  character(len=*), parameter :: rname='SIC'
  integer(kind=4) :: goto_code,nc
  character(len=132) :: comarg
  logical :: all,err,logi,ifconstruct
  integer(kind=4) :: lstart,lend
  character(len=message_length) :: mess
  !
  ! Do not log all these commands: control-flow like IF ENDIF would
  ! fill up the message file:
  ! call sic_message(seve%c,rname,line(1:nline))
  !
  recall = 0       ! No command to execute on return
  nolog = .false.  ! By default, log the command on return
  !
  ! SIC Commands
  select case (command)
  !
  ! IF logical test processing
  case('IF')
    if (ignore_if) then
      nolog = .true.
      return
    endif
    !
    if (if_current.ge.mcif) then
      call sic_message(seve%e,rname,'Too many IF blocks')
      error = .true.
      return
    elseif (lire.eq.0) then
      mess = 'Command '//trim(command)//' is invalid in this context'
      call sic_message(seve%e,rname,mess)
      error = .true.
      return
    else
      call sic_ifconstruct('IF',line,2,.true.,ifconstruct,error)
      if (error)  return
    endif
    !
    ! Evaluate logical expression
    call sic_l4 (line,0,1,logi,.true.,error)
    if (error)  return
    if (ifconstruct) then
      ! IF block construst
      if_current = if_current + 1
      if_last = if_current
      if (logi) then
        if_active(if_current) = .true.
        if_elsefound(if_current) = .false.
        if_finished(if_current) = .false.
      elseif (.not.error) then
        sic_quiet = .true.
        if_active(if_current) = .false.
        if_elsefound(if_current) = .false.
        if_finished(if_current) = .false.
      endif
    else
      ! Logical IF statement (single line)
      if (logi) then
        lstart = sic_start(0,2)
        lend = sic_end(0,sic_narg(0))
        line = line(lstart:lend)
        nline = lend-lstart+1
        recall = 1
      endif
    endif
  !
  ! @ Start procedure execution
  case('@')
    call sic_message(seve%c,rname,line(1:nline))
    if (nliro.ne.-42) nliro = nlire
    liro  = lire
    call exemac (line,error)
    if (error)  return
    if_depth(nlire) = if_current+1 ! Set start depth level
    call sic_log (line,nline,liro)
    if (do_log .and. nliro.ge.1 .and. liro.eq.0) then
      call insert(oline,nolin)
    endif
    nolog = .true.
  !
  ! BEGIN Procedure/Data/Help
  case('BEGIN')
    sic_quiet_proced = sic_quiet
    call begin_area (line,error)
    nolog = .true.
  !
  ! EDIT : Calls stack or file editor
  case('EDIT')
    if (.not.sic_present(0,1)) then
      call edit_stack(error,.true.)
      nolog = .true.
    else
      call sic_ch (line,0,1,comarg,nc,.true.,error)
      if (error)  return
      call sic_parsef(comarg,mess,' ',sicext(1))
      call editor (mess,error)
    endif
  !
  ! EXIT : end SIC keyboard execution (not necessarily program execution)
  case('EXIT')
    call sic_message(seve%c,rname,line(1:nline))
    if (gmaster_get_ismaster().eq.1) then  ! SIC master
      call sic_run_exit(line,ocode,error)
      if (error)  return
#if defined(GAG_USE_PYTHON)
    else  ! Python master
      ! In this case (EXIT or CTRL-D), we only go back to Python prompt.
      ! This seems however a poor man solution, we should probably leave
      ! properly the current execution levels.
      call gpy_interact()
#endif
    endif
  !
  ! FOR : Start Loop execution or compilation
  case('FOR')
    call iniloo (line,nline,error)
    if (error)  return
    gprompt_nlire_old = -10
    if (compil) then
      ifllev (cloop) =  if_last
    else
      call exeloo (error)
      if (error)  return
      call sic_log (line,nline,liro)
      nolog = .true.
    endif
  !
  ! HELP : Display HELP text
  case('HELP')
    call sic_help(line,error)
    if (do_log .and. nliro.ge.1 .and. liro.eq.0) then
      call insert(oline,nolin)
    endif
    nolog = .true.
  !
  ! NEXT : Loop on FOR command
  case('NEXT')
    if (sic_narg(0).gt.1) then
       call sic_message(seve%e,'NEXT','Invalid number of arguments')
       error = .true.
       return
    endif
    if (nlire.eq.1) then
      nolog = .true.
      return
    endif
    if (mlire(nlire).eq.0) then
      if (mlire(nlire-1).eq.-2) then
        nlire=nlire-1
      else
        nolog = .true.
        return
      endif
    elseif (mlire(nlire).ne.-2) then
      nolog = .true.
      return
    endif
    call exeloo (error)
    if (error)  return
    call sic_log (line,nline,liro)
    nolog = .true.
  !
  ! ON : Handle error recovery command
  case('ON')
    call sic_message(seve%c,rname,line(1:nline))
    call sic_on(line,error)
    if (do_log .and. nliro.ge.1 .and. liro.eq.0) then
      call insert(oline,nolin)
    endif
    call sic_log (line,nline,lire)
    nolog = .true.
  !
  ! PAUSE : Interrupt procedure or loop execution
  case('PAUSE')
    if ((lire.lt.0 .or. lire.gt.0).and..not.lverif) then
      mess = ''
      call sic_ch(line,0,1,mess,nc,.false.,error)
      if (error)  continue
      mess = gprompt(1:lgprompt)//trim(command)//"  "//mess(1:nc)
      write (*,'(A)') trim(mess)
    endif
    nliro = nlire
    call break
    if (do_log .and. nliro.ge.1 .and. liro.eq.0) then
      call insert(line,nline)
    endif
    call sic_log (line,nline,lire)
    nolog = .true.
  !
  ! QUIT : Abort (with error) Loop or Procedure execution
  case('QUIT')
    if (nlire.lt.2) then
      nolog = .true.
      return
    endif
    comarg = ' '
    call sic_ke (line,0,1,comarg,nc,.false.,error)
    if (error)  return
    all = comarg.eq.'ALL'
    if (mlire(nlire).ne.0) then
      ! This is probably wrong
      nolog = .true.
      return
    endif
    nlire = nlire-1            ! Leave error processing level
    ! DO ... UNTIL (NLIRE.EQ.1 .OR. .NOT.ALL)
70  error = .true.
    ! Loop
    if (mlire(nlire).le.-2) then
      call finloo
      ! Program
    elseif (mlire(nlire).eq.-1) then
      nlire = nlire-1
      lire = mlire(nlire)
      call reset_if (err)
      ocode = code_end         ! End CODE_EXEC loop
      write(mess,'(A,L)') 'Quitting with error ',error
      call sic_message(seve%i,'QUIT',mess)
      return
      ! Procedure
    elseif (mlire(nlire).ne.0) then
      call endmac
      nlire = nlire-1
      call reset_if (err)
      ! Interactive input
    else
      ! The following line is ABSOLUTELY necessary. It MUST be executed
      ! when one has called interactively a procedure from a PAUSE level, to
      ! ensure that QUIT ALL returns at this interactive level. If this
      ! line is not present, SIC loops forever...
      return
    endif
    ! DO 70 UNTIL (NLIRE.EQ.1 .OR. .NOT.ALL)
    if (all.and.nlire.gt.1) goto 70
  !
  ! BREAK : Abort (without error) Loop execution
  case('BREAK')
    if (mlire(nlire).le.-2) then
      call finloo
    else
      nolog = .true.
    endif
  !
  ! RETURN : Abort (without error) current procedure execution, unless argument
  ! BASE is present. In the latter case, abort all nested procedures.
  case('RETURN')
    call sic_message(seve%c,rname,line(1:nline))
    comarg = ' '
    call sic_ke (line,0,1,comarg,nc,.false.,error)
    if (error)  return
    all = comarg.eq.'BASE'
    error = comarg.eq.'ERROR'
    !
    ! First abort loops from this level if the previous one is a macro
    ! DO 80 ... UNTIL .NOT.(ALL.AND.NLIRE.GT.1)
80  continue
    if (mlire(nlire).eq.0) then
      nolog = .true.
      return
    elseif (mlire(nlire).eq.-2 .and. nlire.gt.2) then
      if (mlire(nlire-1).eq.0) then
        nolog = .true.
        return
      else
        do while (mlire(nlire).eq.-2)
          call finloo
        enddo
      endif
    endif
    if (mlire(nlire).eq.-1) then
      nlire = nlire-1
      lire = mlire(nlire)
      call reset_if (err)
      ocode = code_end         ! End CODE_EXEC loop
      write(mess,'(A,L)') 'Returning with error ',error
      call sic_message(seve%i,'RETURN',mess)
      return
    else
      call endmac
    endif
    nlire = nlire-1
    call reset_if (err)
    ! DO 80 ... UNTIL .NOT.(ALL.AND.NLIRE.GT.1)
    if (all.and.nlire.gt.1) goto 80
  !
  ! TYPE : List stack or file
  case('TYPE')
    call sic_type(line,.true.,goto_code,error)
    if (goto_code.ne.50) then
      ! Do not put in stack
      nolog = .true.
    endif
  !
  ! CONTINUE : Resume interrupted execution
  case('CONTINUE')
    nolog = .true.
    if (lire.ne.0)  return
    if (nlire.eq.1)  return
    nlire = nlire-1
    lire = mlire(nlire)
    call sic_log (line,nline,lire)
  !
  ! PYTHON
  case('PYTHON')
    call sic_message(seve%c,rname,line(1:nline))
    call sic_run_python(line,.true.,error)
    !
  case default
    !
    if (ignore_if) then
      if (command.eq.'ELSE' .or. command.eq.'END') then
        nolog = .true.
        return
      endif
    endif
    call sic_message(seve%e,rname,'Command '//trim(command)//' is invalid in this context')
    error = .true.
    !
  end select
  !
end subroutine sic_run_comm
!
function sic_format_system_token(line,nline)
  use sic_interfaces, except_this=>sic_format_system_token
  use sic_structures
  !---------------------------------------------------------------------
  ! @ private
  ! Detect $ token and reformat the line properly before execution.
  !---------------------------------------------------------------------
  logical :: sic_format_system_token
  character(len=*), intent(inout) :: line
  integer(kind=4),  intent(inout) :: nline
  !
  integer(kind=4) :: idollar
  !
  sic_format_system_token = .false.
  if (compil)  return
  idollar = index(line,'$')
  if (idollar.le.0)  return
  !
  if (idollar.eq.1) then
    sic_format_system_token = .true.
  elseif (line(1:idollar-1).eq.' ') then
    sic_format_system_token = .true.
    line(1:nline) = line(idollar:nline)
    nline = nline-idollar+1
  else
    ! There are other characters before the $ token, so it is not a
    ! system token. Do nothing.
  endif
end function sic_format_system_token
!
subroutine sic_run_exit(line,ocode,error)
  use sic_interfaces, except_this=>sic_run_exit
  use sic_interactions
  use sic_interpreter
  !---------------------------------------------------------------------
  ! @ private
  ! Perform proper actions for the EXIT command
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line
  integer(kind=4),  intent(inout) :: ocode
  logical,          intent(inout) :: error
  ! Local
  integer(kind=4) :: exit_code,i
  logical :: all,err
  !
  ! Command line parsing
  ! Set the 'noprompt' flag according to EXIT /NOPROMPT:
  call gmaster_set_exitnoprompt(sic_present(1,0))
  ! Set the exit code
  exit_code = 0
  call sic_i4(line,0,1,exit_code,.false.,error)
  if (error)  continue  ! ?
  call gmaster_set_exitcode(exit_code)
  !
  all = .false.
  do i=1,nlire
    if (mlire(i).eq.-1) all = .true.
  enddo
  if (.not.all) then
    call gmaster_on_exit(error)
    if (error)  return
    lenter = .false.
    ocode = code_exit        ! Final EXIT for program
    return
  else
90  continue
    ! Loop
    if (mlire(nlire).le.-2) then
      call finloo
      ! Program : return
    elseif (mlire(nlire).eq.-1) then
      nlire = nlire-1
      lire = mlire(nlire)
      call reset_if (err)
      ocode = code_end       ! End CODE_EXEC loop
      return
      ! Procedure
    elseif (mlire(nlire).ne.0) then
      call endmac
      nlire = nlire-1
      call reset_if (err)
    else
      nlire = nlire-1
    endif
    if (nlire.gt.1) goto 90
    lire = 0
    nlire = 0
    call reset_if (err)
    ocode = code_end         ! End CODE_EXEC loop
    return
  endif
  !
end subroutine sic_run_exit
!
subroutine sic_proced(line,nline,error)
  use sic_interfaces, except_this=>sic_proced
  use sic_dependencies_interfaces
  use sic_dictionaries
  use sic_interpreter
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  ! Compilation mode for BEGIN PROC/END PROC blocks
  !---------------------------------------------------------------------
  character(len=*), intent(inout) :: line   ! Input command line
  integer,          intent(inout) :: nline  ! Line length
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=command_length) :: command
  character(len=132) :: comarg
  integer :: nc
  integer, parameter :: mvoc_end=4
  character(len=16) :: vocab_end(mvoc_end),keyword
  ! Data
  data vocab_end /'IF','PROCEDURE','HELP','DATA'/
  !
  if (freelet) then
    ! If 'freelet' is .true., we suspect the command line to be a
    ! FREE SYNTAX assignment. This implies to skip the BEGIN/END
    ! recognition hereafter. The command line written in the
    ! procedure in not translated.
    command = ' '
    !
  else
    call sic_find(command,line,nline,error)
    if (error) then
      call sic_message(seve%d,'SIC','Ignoring error in procedure definition')
      error = .false.
    endif
    !
    if (command.eq.'BEGIN') then
      call sic_message(seve%e,'END','Invalid nested BEGIN/END block')
      error = .true.
      return
      !
    elseif (command.eq.'END') then
      call sic_ke(line,0,1,comarg,nc,.true.,error)
      if (error)  return
      call sic_ambigs('END',comarg,keyword,nc,vocab_end,mvoc_end,error)
      if (error)  return
      !
      if (keyword.eq.'PROCEDURE') then
        call close_procedure(command,line,nline,error)
        sic_quiet = sic_quiet_proced
        return
      elseif (keyword.ne.'IF') then
        call sic_message(seve%e,'END','Invalid END '//trim(keyword)//  &
        ' statement in procedure definition')
        error = .true.
        return
      endif
    endif
  endif
  !
  ! Any command, including END IF
  call write_procedure(command,line,nline,error)
  if (error) then
    call close_procedure(command,line,nline,error)
    sic_quiet = sic_quiet_proced
  endif
  !
end subroutine sic_proced
!
subroutine sic_compil(line,nline,goto_code,error)
  use sic_dependencies_interfaces
  use sic_interpreter
  use sic_dictionaries
  use sic_interactions
  use sic_interfaces, except_this=>sic_compil
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  ! Compilation mode for FOR/NEXT blocks
  !---------------------------------------------------------------------
  character(len=*), intent(inout) :: line       ! Input command line
  integer,          intent(inout) :: nline      ! Line length
  integer,          intent(out)   :: goto_code  ! Where to go on return?
  logical,          intent(inout) :: error      ! Logical error flag
  ! Local
  character(len=1), parameter :: backslash=char(92)
  character(len=command_length) :: command
  character(len=132) :: comarg
  integer :: nc
  character(len=message_length) :: mess
  logical :: ifconstruct
  !
  goto_code = 10
  !
  if (freelet) then
    command = ' '
  else
    call sic_find(command,line,nline,error)
    if (error) then
      command = ' '
      error = .false.
      if (mlire(nlire).eq.0) then
        call sic_message(seve%e,'SIC','Ignoring error in FOR block')
        return  ! Interactive level 0: do not save erroneous command in
                ! the FOR/NEXT block
      endif  ! Else, save it and raise an error at execution for safety
    endif
  endif
  !
  if (command.eq.'BEGIN' ) then
    call sic_message(seve%e,'SIC','Command '//trim(command)//' is invalid in this context')
    error = .true.
    return
  elseif (command.eq.'END') then
    call sic_ke(line,0,1,comarg,nc,.true.,error)
    if (error) then
      return
    elseif  (comarg.ne.'IF') then
      call sic_message(seve%e,'END','Command '//trim(command)//' is invalid in this context')
      error = .true.
      return
    endif
  elseif (command.eq.'QUIT') then
    call aboloo
    gprompt_nlire_old = -10
    mess = 'SIC'//backslash//'QUIT'
    if (do_log) call sic_insert(mess)
    call sic_log(mess,8,liro)
    return
  elseif (command.eq.'HELP'.and.mlire(nlire).eq.0) then
    call sic_help(line,error)
    goto 20
  elseif (command.eq.'TYPE'.and.mlire(nlire).eq.0) then
    call sic_type(line,.false.,goto_code,error)
    goto 20
  endif
  !
  if (ccomm%icom.gt.0 .and. vocab(ccomm%icom)(1:1).eq.'#') then
    call sic_message(seve%w,'FOR','Line not valid in this context, ignored')
    return
  elseif (command.eq.'NEXT') then
    if (ifllev(cloop).ne.if_last) then
      call sic_message(seve%e,'SIC','Invalid nesting of loops and IF')
      error = .true.
      return
    endif
  endif
  !
  if (command.eq.'FOR') then
    call iniloo(line,nline,error)
    if (error) return
    ifllev (cloop) = if_last
  else
    call insloo(line,nline,error)
    if (error) return
    if (command.eq.'IF') then
      call sic_ifconstruct('IF',line,2,.true.,ifconstruct,error)
      if (error)  return
      ! Increment the IF deepness only in case of IF block construct
      ! (as opposed to single-line logical IF statement)
      if (ifconstruct)  if_last = if_last+1
    elseif (command.eq.'END') then   ! END IF checked before
  ! elseif (command.eq.'ENDIF') then
      if_last = if_last-1
    endif
  endif
  !
  call sic_log (line,nline,liro)
  if (do_log) then
    if (nlire.ge.1 .and. lire.eq.0 .and. vocab(ccomm%icom)(1:1).ne.'#')  &
      call insert(line,nline)
  endif
  if (error) return
  !
  if (command.eq.'NEXT') then
    if (sic_narg(0).eq.1) then
      call verify_loop_variable (cloop,line,error)
      if (error) return
    else if (sic_narg(0).gt.1) then
      call gag_message(seve%e,'NEXT','Wrong number of arguments')
      error = .true.
      return
    endif
    if (cloop.eq.ifloop(nlire+1)+1) then
      indice(cloop) = 0.0
      nlire = nlire + 1
      mlire(nlire) = -2
      if_depth(nlire) = if_current+1
      compil = .false.
      lasllo (cloop) = loolen
      lasblo (cloop) = bulend
      call exeloo (error)
      gprompt_nlire_old = -10
    else
      call delete_loop_variable (cloop)
      lasllo (cloop) = loolen
      lasblo (cloop) = bulend
      cloop = ploop(cloop)
    endif
  endif
  !
  return
  !
20 goto_code = 20
  return
end subroutine sic_compil
!
subroutine sic_ifblock(line,nline,command,goto_code,error)
  use gbl_message
  use sic_interfaces, except_this=>sic_ifblock
  use sic_interactions
  use sic_interpreter
  use sic_structures
  !---------------------------------------------------------------------
  ! @ private
  ! IF block exploration
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line       ! Command line
  integer(kind=4),  intent(in)    :: nline      ! Length of command line
  character(len=*), intent(in)    :: command    !
  integer(kind=4),  intent(out)   :: goto_code  !
  logical,          intent(inout) :: error      ! Logical error flag
  ! Local
  character(len=32) :: comarg
  integer(kind=4) :: nc
  logical :: ifconstruct,dolog
  !
  goto_code = 10  ! Default: error handling
  !
  ! If exploring an IF block, look for special commands
  if (command.eq.'BEGIN') then
    call sic_message(seve%e,'SIC','Command '//trim(command)//' is invalid in this context')
    error = .true.
    return
  elseif (command.eq.'END') then
    call sic_ke(line,0,1,comarg,nc,.true.,error)
    if (error) then
      return
    elseif (comarg.ne.'IF') then
      call sic_message(seve%e,'END','Command '//trim(command)//' is invalid in this context')
      error = .true.
      return
    endif
  endif
  !
  if (.not.if_active(if_current)) then
    ! The active part of the IF block was not yet found. Check for IF,
    ! ELSE[IF] or ENDIF, and activate the coming part if relevant.
    if (command.eq.'IF') then
      call sic_ifconstruct('IF',line,2,.true.,ifconstruct,error)
      if (error)  return
      ! Increment the IF deepness only in case of IF block construct
      ! (as opposed to single-line logical IF statement)
      if (ifconstruct)  if_last = if_last+1
    elseif (if_last.eq.if_current) then
      call sic_ifblock_elseend(line,nline,command,dolog,error)
      if (error)  return
      if (dolog)  goto 50
    elseif (command.eq.'END') then
      if_last = if_last-1
    endif
    ! Skip all other commands
    goto 20
    !
  elseif (.not.if_finished(if_current)) then
    ! The active part of the IF block was already found (if_active(if_current)
    ! == .true.) but it is not yet finished (if_finished(if_current) ==
    ! .false.). In other words, we are currently executing it. Check for ELSE[IF]
    ! or ENDIF, and deactivate the current part if relevant. Otherwise execute
    ! the command since we are in the active block.
    !
    ! Looking for ELSE, ELSE IF, or ENDIF
    call sic_ifblock_elseend(line,nline,command,dolog,error)
    if (error)  return
    if (dolog)  goto 50
    ! Execute all other commands
    !
  else
    ! The active part of the IF block was already found (if_active(if_current)
    ! == .true.) and it is already finished (if_finished(if_current) ==
    ! .true.). Check for new IF subblock (inactive since this part is inactive),
    ! ELSE[IF] or ENDIF, and leave the current IF block if relevant.
    !
    if (command.eq.'IF') then
      call sic_ifconstruct('IF',line,2,.true.,ifconstruct,error)
      if (error)  return
      ! Increment the IF deepness only in case of IF block construct
      ! (as opposed to single-line logical IF statement)
      if (ifconstruct)  if_last = if_last+1
      !
    elseif (if_last.eq.if_current) then
      call sic_ifblock_elseend(line,nline,command,dolog,error)
      if (error)  return
      if (dolog)  goto 50
      !
  ! elseif (command.eq.'ENDIF') then
    elseif (command.eq.'END') then
      if_last = if_last-1
    endif
    ! Skip all other commands. Skip in particular ELSE and ELSE IF statements
    ! in non active IF subblocks.
    goto 20
  endif
  goto_code = 0
  return
  !
20 goto_code = 20
  return
50 goto_code = 50  ! Same as goto_code 0 except command line is logged
  return
  !
end subroutine sic_ifblock
!
subroutine sic_ifblock_elseend(line,nline,command,dolog,error)
  use gbl_message
  use sic_interfaces, except_this=>sic_ifblock_elseend
  use sic_interactions
  use sic_interpreter
  use sic_structures
  !---------------------------------------------------------------------
  ! @ private
  !  Parsing of END or ELSE [IF] in an IF block
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line
  integer(kind=4),  intent(in)    :: nline
  character(len=*), intent(in)    :: command
  logical,          intent(out)   :: dolog
  logical,          intent(inout) :: error
  ! Local
  character(len=32) :: comarg
  integer(kind=4) :: nc
  logical :: ifconstruct,logi
  !
  ! NB: THIS MAKES SENSE ONLY IF if_last == if_current
  !
  dolog = .false.
  if (command.eq.'END') then  ! Caller checked this is a END IF
    sic_quiet = .false.
    if_active(if_current) = .false.
    if_elsefound(if_current) = .false.
    if_finished(if_current) = .true.
    if_current = if_current-1
    if_last = if_current
    if (lverif .and. lire.ne.0)  &
      call sic_echo (gprompt,lgprompt,line,nline)
    dolog = .true.
    !
  elseif (command.eq.'ELSE') then
    !
    comarg = ' '
    call sic_ke(line,0,1,comarg,nc,.false.,error)
    if (error) return
    !
    if (comarg.eq.' ') then
      ! ELSE (no argument)
      if (if_elsefound(if_current)) then
        call sic_message(seve%e,'ELSE','Duplicate ELSE statements in IF-ENDIF block')
        error = .true.
        return
      endif
      if_elsefound(if_current) = .true.
      if (.not.if_active(if_current)) then
        ! The active part of the IF block was not yet found. This ELSE part
        ! becomes the active one:
        sic_quiet = .false.
        if_active(if_current) = .true.
        if_finished(if_current) = .false.
      else
        ! The active part of the IF block was already found. Finish it (may be
        ! it was already finished earlier, harmless doing it twice)
        sic_quiet = .true.
        if_finished (if_current) = .true.
      endif
      !
    elseif (comarg.eq.'IF') then
      ! ELSE IF: check syntax
      if (if_elsefound(if_current)) then
        call sic_message(seve%e,'ELSE','ELSE IF statement cannot follow ELSE statement')
        error = .true.
        return
      endif
      call sic_ifconstruct('ELSEIF',line,3,.false.,ifconstruct,error)
      if (error)  return
      if (.not.ifconstruct) then
        call sic_message(seve%e,'ELSEIF','Invalid IF statement for an ELSEIF')
        error = .true.
        return
      endif
      if (.not.if_active(if_current)) then
        ! The active part of the IF block was not yet found. Is this ELSEIF active?
        call sic_l4 (line,0,2,logi,.true.,error)
        if (error)  return
        if (logi) then
          sic_quiet = .false.
          if_active(if_current) = .true.
          if_finished(if_current) = .false.
        endif
      else
        ! The active part of the IF block was already found. Finish it (may be
        ! it was already finished earlier, harmless doing it twice)
        sic_quiet = .true.
        if_finished (if_current) = .true.
      endif
      !
    else
      call sic_message(seve%e,'ELSE','Invalid argument '//comarg(1:12))
      error = .true.
      return
    endif
    if (lverif .and. lire.ne.0)  &
      call sic_echo (gprompt,lgprompt,line,nline)
    !
    dolog = .true.
  endif
  ! Do nothing here for all other commands
  !
end subroutine sic_ifblock_elseend
!
subroutine reset_if(error)
  use sic_interfaces, except_this=>reset_if
  use sic_structures
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  logical, intent(out) :: error  !
  ! Local
  integer :: i,ifgood
  !
  ! New corrected logic
  ifgood = if_depth(nlire+1)-1
  !
  if (if_current.gt.ifgood) then
    error = .true.
    do i=ifgood+1,if_current
      if_active(i) = .false.
      if_elsefound(i) = .false.
      if_finished(i) = .true.
    enddo
    if_current = ifgood
    if_last = ifgood
  else
    error = .false.
  endif
  sic_quiet = .false.
end subroutine reset_if
!
subroutine sic_ifconstruct(rname,iline,iarg,allowed,ifconstruct,error)
  use gildas_def
  use gbl_message
  use sic_interfaces, except_this=>sic_ifconstruct
  use sic_structures
  use sic_types
  !---------------------------------------------------------------------
  ! @ private
  !  Detect if command line "[ELSE] IF (Logi) ..." is an IF statement
  ! or an IF construct
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: rname        ! Calling routine name
  character(len=*), intent(in)    :: iline        ! Input command line
  integer(kind=4),  intent(in)    :: iarg         !
  logical,          intent(in)    :: allowed      ! IF statement allowed or not?
  logical,          intent(out)   :: ifconstruct  ! Is an IF construct or not?
  logical,          intent(inout) :: error        ! Logical error flag
  ! Local
  character(len=5) :: cthen  ! Length 5 on purpose (i.e. typo THENN would be a syntax error)
  integer(kind=4) :: nc
  character(len=commandline_length) :: line,buff
  type(sic_command_t), save :: command  ! Save'd to enable the optimization in sic_parse_line
  !
  cthen = 'THEN'
  call sic_ke(iline,0,iarg,cthen,nc,.false.,error)
  if (error)  return
  ifconstruct = cthen.eq.'THEN'
  if (ifconstruct) then
    if (sic_narg(0).gt.iarg) then
      call sic_message(seve%e,rname,'Trailing arguments after THEN')
      error = .true.
    endif
    return
  endif
  !
  ! Found a candidate IF statement
  if (allowed) then
    ! Check if it is a valid command line
    line = iline(sic_start(0,iarg):)
    nc = len_trim(line)
    call replace_symbol(line,nc,error)
    if (error) return
    call sic_parse_line(line,nc,.true.,sic_quiet,command,buff,error)
    if (error)  return
  else
    ! Forbidden => syntax error
    call sic_message(seve%e,rname,'Invalid syntax')
    error = .true.
    return
  endif
  !
end subroutine sic_ifconstruct
!
subroutine setlet(line,nline,freelet,error)
  use sic_interfaces, except_this=>setlet
  use sic_interactions
  use sic_dictionaries
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  !  Check if input line is a free syntax assignment (e.g "A = 1"). If
  ! yes, add a "SIC\LET" command at the beginning of the line.
  ! If not in execution mode (i.e. COMPIL and PROCED), we cannot check
  ! if variable really exists.
  !---------------------------------------------------------------------
  character(len=*), intent(inout) :: line     ! Modified on return
  integer,          intent(inout) :: nline    !
  logical,          intent(inout) :: freelet  ! Can possibly be a free syntax assignment?
  logical,          intent(inout) :: error    !
  ! Local
  character(len=1), parameter :: backslash=char(92)
  character(len=256) :: var
  character(len=2048) :: line1
  integer :: i,j,k,mcom,jline
  logical :: found
  !
  i = index(line(1:nline),' ')
  if ((i.eq.0).or.(i+2.ge.nline)) return
  if (line(i:i+2).ne.' = ') return
  !
  ! \ is present, must be a command
  if (index(line(1:i),backslash).ne.0) return
  !
  ! In procedure mode and compilation of loop, we can not rely on
  ! variable existence test. The line must be translated later on.
  if (proced .or. compil) then
    freelet = .true.
    return
  endif
  !
  ! Check if it is a known variable. Do not bother checking
  ! validity of subarrays.
  var = line(1:i-1)
  jline = index(var,'[')
  if (jline.eq.0) then
    jline = i-1
  else
    jline = jline-1
  endif
  !
  ! Check if variable exist
  found = sic_level(var(1:jline)).ge.0
  if (.not.found) return
  !
  ! Known variable
  !
  ! Check if it is a known command
  if (jline.lt.len(vocab(1))) then
    do k=1,nlang
      if (.not. (languages(k)%asleep.or.languages(k)%libmod) ) then
        j = mbuilt(k-1)
        mcom = mbuilt(k)
        do while (j.lt.mcom)
          j = j+1
          !
          ! Return an error if exact match: syntax is ambigous
          if (sic_eqchain(var(1:jline),vocab(j)(2:))) then
            call sic_message(seve%e,'INTER',var(1:jline)//' is ambiguous: '//  &
            'command or variable ?')
            error = .true.
            return
          endif
          j = j+nopt(j)
        enddo
      endif
    enddo
  endif
  !
  ! A true variable: assume assignment...
  line1 = 'SIC'//backslash//'LET '//line
  nline = min(len(line),nline+8)
  line = line1
end subroutine setlet
