One pseduo code solution:

PROCESS QUERY
    // Create two counters initially set to NULL:
    counters_t *andSequence // holds result of current AND operations
    counters_t *orSequence  // accumulates the final result combining OR sequences
    shortCircuit = false    // stop early if AND sequence will fail

   Loop over all words in query
      if word == OR 
         // orSequence holds final result, merge in andSequence seen so far
         // think of stepping back after previously stepping away to calculate AND
         MERGE(andSequence, orSequence) 
         shortCicuit = false  //reset shortCircuit
         continue
      if shortCircuit 
         // at least one word returned no results, so AND sequence will fail
         // skip the words until see OR and shortCircuit is reset
         continue 
      if word == AND
         continue to next word // same as if AND wasn't there
      else found non operator word 
         // think of stepping away to calculate AND sequence
         get pages for this word with match = index_find(word)
         if match == NULL  // no pages contain this query word
            shortCircuit = true // AND will fail for next word, so skip it and others until see OR
            if andSequence != NULL 
               // because this word returned no pages, drop everything AND'ed so far
               delete andSequence
               andSequence = NULL
         else  // found counters for this word in index
            if andSequence == NULL // this is the first word of AND sequence
               andSequence = new counters
               UNION(andSequence, match)  //essentially copies match to andSequence
            else // not first word in AND sequence
               INTERSECT(andSequence, match) //intersect this word's pages 

   MERGE(andSequence, orSequence)
   return orSequence



MERGE(andSequence, orSequence):
    // push temporary result andSequence to final result orSequence
    // also reset andSequence
    if andSequence != NULL
       if orSequence == NULL
          orSequence = new counters
       UNION(orSequence, andSequence) //union andSequence into orSequence
       delete andSequence and set to NULL

UNION(orSequence, otherSequence)
    // union orSequence and otherSequence by adding counts for matching keys
    // store results in orSequence
    counters_iterate(otherSequence, orSequence, unionFunction)

unionFunction(arg, key, count)
   // update arg counters 
   wordCount = counters_get(arg, key) // returns 0 if key not found
   counters_set(arg, key, count + wordCount)

INTERSECT(orSequence, otherSequence)
  // intersect orSequence and otherSequence by taking of counts for matching keys
  // store results in orSequence
  struct twocounters args = {orSequence, otherSequence}
  counters_iterate(orSequence, args, intersectFunction)

intersectFunction(arg, key, count)
    counters_set(arg->orSequence, key, min(count, counters_get(args->otherSequence, key)))