;+
; NAME:
;	PARSE_OP
;
;
; PURPOSE:
;	Convert a description of a set of ops into code and execute it.
;
;
; CATEGORY:
;	OPS
;
;
; CALLING SEQUENCE:
;	seq = parse_op(descr)
;
;
; INPUTS:
;	descr	string	The description of the operation sequence.
;
;
; OPTIONAL INPUTS:
;
;
;
; KEYWORD PARAMETERS:
; 	/noexec		If specified, then ony generate the code, do
;		 	not try to run it. (The CODE key must then be
;		 	set).
;	code	string	A named variable to contain the IDL code
;			generated by parsing the descriptor.
;	file	string	A file from which to read the description.
;	Name mappings may be given as keys: for example if you have a
;	sequence called EVNT, but the descriptor expects SS then the
;	key SS=EVNT may be given.	
;
;
; OUTPUTS:
;	seq	objref	The smei_sequence generated from the descriptor.
;
;
; RESTRICTIONS:
;	The descriptor argument and the file spec are exclusive.
;
;
; MODIFICATION HISTORY:
;	Original: 30/10/03; SJT
;	Allow passing of remappings, and descriptor as a file:
;	4/11/03; SJT
;	Fix indexing error that caused complex expressions to fail:
;	30/3/04; SJT
;-

function parse_op, descr, noexec = noexec, code = code, file = file, $
                   _extra = _extra

if keyword_set(noexec) and not arg_present(code) then begin
    smei_msg, /alert, ["The /NOEXEC key requires the CODE key to be " + $
                       "present",  $
                       "and changeable"]
    return, 0
endif

atom = "\([^()]+\)"             ; A descriptor element regex.

if keyword_set(file) + n_params() ne 1 then begin
    smei_msg, /alert, ["You must give either an explicit", $
                       "descriptor string or the FILE key", $
                       "but not both"]
    return, 0
endif

if keyword_set(file) then descr = read_smei_op(file)

dcopy = descr

if keyword_set(_extra) then begin
    elist = tag_names(_extra)
    for j = 0, n_elements(elist)-1 do $
      ok = execute(elist[j]+' = _extra.'+elist[j])
    depth = scope_level()       ;routine_names(/level)
endif

itoken = 0
while stregex(dcopy, atom, /bool) do begin
    s = stregex(dcopy, atom, length = l)
    ctoken =  parse_op_token(strmid(dcopy, s+1, $
                                    l-2), itoken, get_level = depth)
    if itoken eq 0 then code = ctoken $
    else code = [code, ctoken]
    dcopy = strmid(dcopy, 0, s)+ $
      '#token'+string(itoken, format = "(I0)")+ $
      strmid(dcopy, l+s, strlen(dcopy))
    itoken = itoken+1
endwhile

if itoken eq 0 or keyword_set(noexec) then return, 0

for j = 0, itoken-1 do begin
    ok = execute(code[j])
    if not ok then begin
        smei_msg, /alert, ['Failed to execute:', $
                           code[j], $
                           !error_state.msg, !error_state.sys_msg]
        return, 0
    endif
endfor
ok = execute('rv = token'+string(itoken-1, format = "(i0)"))

for j = 0, itoken-2 do $
  ok = execute('obj_destroy, token'+string(j, format = "(I0)"))

return, rv

end
