/*{{{}}}*/
/*{{{  #includes*/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define PARSECOND_C
#define I_SET_C

#include "keybind.h"
#include <lib/ori_add_lib.h>
/*}}}  */

/*{{{  parse_cond*/
/*{{{  l_parse_cond*/
private TOKEN* l_parse_cond(TOKEN *buff,tokens to,boolean allow_comma,int dummy_base,boolean *ret_used)
{
  for (;;)
   { check_length(buff);
     switch(to) {
       /*{{{  comma*/
       case COMMA:
        { if (allow_comma)
           /*{{{  ignore it*/
           { to=get_full_token();
             continue;
           }
           /*}}}  */
          else
           /*{{{  error*/
             m_exit(M_NOTEST);
           /*}}}  */
        }
       /*}}}  */
       /*{{{  single tests*/
       case EDITING:         *buff++ = M_EDITING;            break;
       case TEST_GEN_FOLD:   *buff++ = M_TEST_FOLD;          break;
       case TEST_FILED:      *buff++ = M_FILED_FOLD;         break;
       case TEST_FOLD_LINE:  *buff++ = M_CLOSED_FOLD;        break;
       case TEST_BEGIN_FOLD: *buff++ = M_BEGIN_FOLD_COMMENT; break;
       case TEST_END_FOLD:   *buff++ = M_END_FOLD_COMMENT;   break;
       case TEST_TEXT:       *buff++ = M_TEXTLINE;           break;
       case TEST_TOP:        *buff++ = M_TOP_OF_FOLD;        break;
       case TEST_BOTTOM:     *buff++ = M_BOT_OF_FOLD;        break;
       case TEST_BEGIN_LINE: *buff++ = M_BEGIN_OF_LINE;      break;
       case TEST_END_LINE:   *buff++ = M_END_OF_LINE;        break;
       case TEST_CHANGED:    *buff++ = M_CHANGED;            break;
       case TEST_OVER:       *buff++ = M_TEST_OVER;          break;
       case TEST_HASH:       *buff++ = M_TEST_HASH;          break;
       case TEST_VERBOSE:    *buff++ = M_TEST_VERBOSE;       break;
       case TEST_VIEW:       *buff++ = M_TEST_VIEW;          break;
       case TEST_AUTO:       *buff++ = M_TEST_AUTO;          break;
       case TEST_ECHO:       *buff++ = M_TEST_ECHO;          break;
       /*}}}  */
       /*{{{  test-str*/
       case TEST_STR:
          *buff++ = M_TEST_STR;
          if (!(buff=get_message(buff)))
             m_exit(M_NOPROMPT);
          *buff++ = M_END_MACRO;
          break;
       /*}}}  */
       /*{{{  test with one counter/term argument*/
       case TEST_CHAR_LOW_C:
       case TEST_CHAR_HIGH_C:
       case TEST_CHAR_C:
        { tokens token;
          boolean d=False;
#         define dummy_off (def_dummy_vars/2)

          token=name_to_var(get_full_token(),0,new_vars_enabled);
          if (token!=VARIABLE || tk_var->size!=1)
           { if (!(buff=parse_term(token,buff,dummy_var(dummy_base+dummy_off),dummy_base+dummy_off+1,ret_used))) return(0);
             d=True;
           }
          *buff++ =
                    (to==TEST_CHAR_C)         ? M_TEST_CC :
                    (to==TEST_CHAR_HIGH_C)    ? M_TEST_H_CC :
                                                M_TEST_L_CC;
          *buff++=(TOKEN)(d?dummy_var(dummy_base+dummy_off):tk_var->no);
          break;
        }
       /*}}}  */
       /*{{{  test with names*/
       case TEST_TERM:
          switch (get_full_token())
           { case MACRO:
             case NAME:
              /*{{{  test a single terminal*/
              { unsigned char *s;

                *buff++=M_ENV_CMD;
                *buff++=(TOKEN)0;
                for (s=tk_string;*s;)
                   *buff++=(TOKEN)*s++;
                *buff++=M_END_MACRO;
                break;
              }
              /*}}}  */
             case BEGIN:
              /*{{{  test a list of terminals*/
              { unsigned char *s;
                TOKEN *first_ad;

                for (first_ad=0;;)
                 { switch (get_full_token())
                    { case MACRO:
                      case NAME:
                         *buff++=M_ENV_CMD;
                         *buff++=(TOKEN)0;
                         for (s=tk_string;*s;)
                            *buff++=(TOKEN)*s++;
                         *buff++=M_END_MACRO;
                         if (!first_ad)
                            first_ad=buff;
                         buff=generate_jmp(M_JMP_TRUE,buff,first_ad);
                         continue;
                      case END:
                         if (!first_ad)
                          /*{{{  set flag to False*/
                          { buff=generate_jmp(M_JMP_FALSE,first_ad=buff,buff);
                            *buff++=M_NOT;
                            generate_jmp(M_JMP_FALSE,first_ad,buff);
                          }
                          /*}}}  */
                         else
                          /*{{{  backpatch first jump*/
                            generate_jmp(M_JMP_TRUE,first_ad,buff);
                          /*}}}  */
                         break;
                      default:
                         goto t_t_err;
                    }
                   break;
                 }
                break;
              }
              /*}}}  */
             default:
             t_t_err:
                m_exit(M_T_NAME);
           }
          break;
       /*}}}  */
       /*{{{  relops*/
       case REL_EQ:
       case REL_LOW:
       case REL_LOW_EQ:
       case REL_HIGH:
       case REL_HIGH_EQ:
       case REL_NEQ:
        { int arg1,arg2;
          tokens arg_token;
          TOKEN *jmp_end=0;

          begin_parse();
          /*{{{  get first argument*/
          arg1=dummy_var(dummy_base++);
          if (!(buff=parse_term(get_full_token(),buff,arg1,dummy_base,ret_used))) return(0);
          /*}}}  */
          arg_token=name_to_var(get_full_token(),0,new_vars_enabled);
          do
           { *buff++=M_INV_COUNTER;
             *buff++=(TOKEN)arg1;
             /*{{{  get second argument*/
             if (arg_token==VARIABLE && tk_var->size==1)
                arg2=tk_var->no;
             else
              { arg2=dummy_var(dummy_base);
                if (!(buff=parse_term(arg_token,buff,arg2,dummy_base+1,ret_used))) return(0);
              }
             /*}}}  */
             arg_token=name_to_var(get_full_token(),0,new_vars_enabled);
             if (arg_token!=END)
              /*{{{  save arg2*/
              { *buff++=M_PUSH_INT;
                *buff++=(TOKEN)arg2;
                *buff++=M_POP_INT;
                *buff++=(TOKEN)(arg2=rel_op_var);
              }
              /*}}}  */
             /*{{{  arg1=arg2-arg1*/
             *buff++=M_SUM_COUNTER;
             *buff++=(TOKEN)arg1;
             *buff++=(TOKEN)arg2;
             /*}}}  */
             /*{{{  test for rel*/
             switch (to)
              { case REL_EQ:
                case REL_NEQ:
                   *buff++=M_NULL_COUNTER;
                   *buff++=(TOKEN)arg1;
                   break;
                case REL_LOW_EQ:
                case REL_HIGH:
                   *buff++=M_INV_COUNTER;
                   *buff++=(TOKEN)arg1;
                default:
                   *buff++=M_POSITIV_COUNTER;
                   *buff++=(TOKEN)arg1;
              }
             switch(to)
              { case REL_NEQ:
                case REL_LOW_EQ:
                case REL_HIGH_EQ:
                   *buff++=M_NOT;
                case REL_LOW:
                case REL_HIGH:
                case REL_EQ:
                default:
                   break;
              }
             /*}}}  */
             if (arg_token!=END)
              /*{{{  generate jmp and get arg1 back*/
              { if (!jmp_end) jmp_end=buff;
                buff=generate_jmp(M_JMP_FALSE,buff,jmp_end);
                *buff++=M_PUSH_INT;
                *buff++=(TOKEN)arg2;
                *buff++=M_POP_INT;
                *buff++=(TOKEN)arg1;
              }
              /*}}}  */
           }
          while (arg_token!=END);
          if (jmp_end)
             generate_jmp(M_JMP_FALSE,jmp_end,buff);
          break;
        }
       /*}}}  */
       /*{{{  testset*/
       case TEST_CHAR_SET:
         *buff++ = M_TEST_CHAR_SET;
         *buff++ =(TOKEN)get_set();
         break;
       /*}}}  */
       /*{{{  testlanguage*/
       case TEST_LANG:
        { *buff++ = M_LANGUAGE;
          if ((to=get_full_token())!=MACRO || ustrlen(tk_string)!=1)
             m_exit(M_NOTEST);
          /*{{{  check valid lg-tag*/
          { char **s;

            tk_string[0]=toupper(tk_string[0]);
            for (s=f_c_name;;)
               if (**s==(char)tk_string[0])
                  break;
               else if (**s)
                  s++;
               else
                  m_exit(M_NOTEST);
          }
          /*}}}  */
          *buff++=(TOKEN)tk_string[0];
          break;
        }
       /*}}}  */
       /*{{{  not condition*/
       case NOT:
         begin_parse();
         if (!(buff=l_parse_cond(buff,get_full_token(),False,dummy_base,ret_used)))
            return(0);
         *buff++ = M_NOT;
         end_parse(M_WANTEND);
         break;
       /*}}}  */
       /*{{{  and/or (c[,c...])*/
       case AND:
       case OR: {
         TOKEN jmp= to==AND ? M_JMP_FALSE : M_JMP_TRUE;
         TOKEN *bp;

         begin_parse();
         if (!(buff=l_parse_cond(buff,get_full_token(),True,dummy_base,ret_used)))
            return(0);
         bp=buff;
         /*{{{  scan all and-arguments up to )*/
         while ((to=get_full_token())!=END) {
           buff=generate_jmp(jmp,buff,bp);
           if (!(buff=l_parse_cond(buff,to,True,dummy_base,ret_used)))
              return(0);
         }
         /*}}}  */
         if (to!=END)
            m_exit(M_WANTEND);
         /*{{{  backpatch the jumps*/
         generate_jmp(jmp,bp,buff);
         /*}}}  */
         break;
       }
       /*}}}  */
       /*{{{  pre*/
       case PRE: {
         /*{{{  push dummy vars*/
         { int i;

           for (i=0;i<dummy_base;i++)
            { *buff++=M_PUSH_INT;
              *buff++=(TOKEN)dummy_var(i);
            }
         }
         /*}}}  */
         if (!(buff=parse_macro(buff,0,ret_used))) return(0);
         /*{{{  pop dummy vars*/
         { int i;

           for (i=dummy_base-1;i>=0;i--)
            { *buff++=M_POP_INT;
              *buff++=(TOKEN)dummy_var(i);
            }
         }
         /*}}}  */
         if (!(buff=l_parse_cond(buff,get_full_token(),False,dummy_base,ret_used)))
            return(0);
         break;
       }
       /*}}}  */
       case ERROR: return(0);
       /*{{{  lastmessage*/
       case LASTMES:
        { *buff++ = M_LASTMES;
          if (get_full_token()==NAME)
           /*{{{  look for msg*/
           { int msg_no;

             if ((msg_no=name2msg(tk_string))>=0)
              { *buff++=msg_no;
                break;
              }
           }
           /*}}}  */
          m_exit(M_NOTEST);
        }
       /*}}}  */
       /*{{{  variable: compile it like ``not(counter-0 variable)''*/
       case VARIABLE:
         if (tk_var->size==1)
          { *buff++ = M_NULL_COUNTER;
            *buff++ = (TOKEN) tk_var->no;
            *buff++ = M_NOT;
            break;
          }
         else
            goto term_as_boolean;
       /*}}}  */
       /*{{{  default use it as term*/
       term_as_boolean:
       default:
          if (!(buff=parse_term(to,buff,dummy_var(dummy_base),dummy_base+1,ret_used))) return(0);
          *buff++ = M_NULL_COUNTER;
          *buff++ = (TOKEN) dummy_var(dummy_base);
          *buff++ = M_NOT;
          break;
       /*}}}  */
     }
     return(buff);
   }
}
/*}}}  */

public TOKEN *parse_cond(TOKEN *buff,int dummy_base,boolean *ret_used)
{
  return
   ( l_parse_cond
      ( buff,
        name_to_var
         ( get_full_token(),
           0,
           new_vars_enabled
         ),
        False,
        dummy_base,
        ret_used
      )
   );
}
/*}}}  */
