;+
; Procedure: evaluate
;
; Purpose:  This routine performs the actual evaluation of an expression in the mini_language
;           It basically combines an slr shift/reduce parser with an evaluator
;           
; Description(of how it works, super-concise version):
;   This routine is essentially a table-driven stack machine.  
;   It uses two tables action & goto.(both are 2-d arrays) to determine how to behave at any given step.  Prior states are pushed onto the stack.
;   At each step, an action is performed, determined by the action table, and a new state is pushed onto the stack, determined by the goto table.
;   Some actions may also pop a certain number of states off of the stack.
;    
;   Actions can be either shift actions or reduce actions(using reduction N). 
;     A shift action, means that it doesn't have enough tokens yet to determine the next operation, so it should process an additional token.
;     A reduce action, means that it should perform some operation.  Replacing a complex expression with a simplified expression by using one of the production rules.
;      
;   One more note, the stack has flexible type.  Each push or pop operation generally pushes or pops two items.  One will be a numerical state(long int), another will be a data item.
;     The data items are often implemented as structs but can represent various types of items. (tokens, intermediate processing results, outputs, operators, variable identifiers)
;     Generally the data items on the stack are anything that might be required as an input to perform a reduction.
;           
; Inputs:
;    tk_list:  a list of token structures from the lex routine
;    grammar:  a grammar description structure
;    parse_tables: a parse table structure
;    
; Keywords: error: On error this routine returns a structure that describes the error that occurred
;
; $LastChangedBy: pcruce $
; $LastChangedDate: 2013-05-10 17:04:22 -0700 (Fri, 10 May 2013) $
; $LastChangedRevision: 12331 $
; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/tags/spedas_3_1/general/mini/evaluate.pro $
;-

pro evaluate,tk_list,grammar,parse_tables,error=error

  compile_opt idl2
  
  evaluator_routines
  
  stk = csstack(parse_tables.initial_state)
  
  i = 0
  
  while 1 do begin
  
    s = csstack(stk,/peek)
    
    if i ge n_elements(tk_list) then begin
      message,'parse error'
    endif
    
    if i eq 0 then begin
      previous = 0
    endif else begin
      previous = tk_list[i-1]
    endelse  
    
    tk = eval_token(tk_list[i],previous,grammar)

    if is_shift_code(parse_tables.action_table[s,tk.index]) then begin
    
      s = get_shift_num(parse_tables.action_table[s,tk.index])
    
      if s eq -1 then begin
        message,'shift code error'
      endif
    
      stk = csstack(tk,stk,/push)
      
      stk = csstack(s,stk,/push)
      
      i++
          
    endif else if is_reduce_code(parse_tables.action_table[s,tk.index]) then begin
    
      red = get_reduce_num(parse_tables.action_table[s,tk.index])
      
      if red[0] eq -1 then begin
        message,'reduce code error'
      endif
    
      reduction = grammar.production_list[red]
      
      ;do reduction here
      
      for j = reduction.length,1,-1 do begin
        stk = csstack(stk,/pop)
        (scope_varfetch('input'+strtrim(string(j),2),level=0)) = csstack(stk,/peek)
        stk = csstack(stk,/pop)
      endfor
        
      catch,err
        
      if err eq 0 then begin
        
      ;  dprint,reduction.fun,reduction.length
        
        if reduction.length eq 1 then begin
          output = call_function(reduction.fun,input1)
         ; dprint,input1
        endif else if reduction.length eq 2 then begin
          output = call_function(reduction.fun,input1,input2)
         ; dprint,input1,input2
        endif else if reduction.length eq 3 then begin
          output = call_function(reduction.fun,input1,input2,input3)
         ; dprint,input1,input2,input3
        endif else if reduction.length eq 4 then begin
          output = call_function(reduction.fun,input1,input2,input3,input4)
          ;dprint,input1,input2,input3,input4
        endif else begin
          message,'unhandled reduction length'
        endelse
        
      endif else begin
        
        help, /Last_Message, Output=theErrorMessage
        catch,/cancel
        error = {type:'error',name:'reduction error',value:theErrorMessage,reduction:red}
        return
        
      endelse
     
      catch,/cancel
     
      s = csstack(stk,/peek)
      stk = csstack(output,stk,/push)
      idx = where(reduction.left eq grammar.nonterminals)
      s2 = long(parse_tables.goto_table[s,idx])
      stk = csstack(s2,stk,/push)
      
    endif else if parse_tables.action_table[s,tk.index] eq 'acc' then begin
    
      ;parsing completed! we're done
      return
      
    endif else if parse_tables.action_table[s,tk.index] eq 'err' then begin
    
      ;unspecified error
      error = {type:'error',name:'User statement syntax error',state:s,token:tk}
      return
      
    endif else begin
      message,'Unhandled parse table entry'
    endelse
  
  endwhile
  
end