def step(self):
     if self.debug:
         print 'iteration', self.iteration
         print 'dna =', limit_string(self.dna)
         
     
     try:
         p = self.pattern()
         if self.debug:
             print 'pattern ', ''.join(map(str, p))
         t = self.template()
         if self.debug:
             print 'template', ''.join(map(str, t))
             
         for pp in p:
             self.pattern_freqs[type(pp)] += 1
         for tt in t:
             self.template_freqs[type(tt)] += 1
             
     finally:
         index = self.parser.index
         self.cost += index
         self.parser = None
         self.item_starts = []
         del self.dna[:index]
         
     self.matchreplace(p, t)
     self.parser = DNA_parser(self.dna, self.freqs)
     self.iteration += 1
     
     if self.debug:
         print 'len(rna) =', len(self.rna)
         print
    def __init__(self, dna):
        #assert all(c in 'ICFP' for c in dna)
        self.pattern_freqs = defaultdict(int)
        self.template_freqs = defaultdict(int)
        self.codon_len_freqs = defaultdict(int)
        self.freqs = [self.pattern_freqs, self.template_freqs, self.codon_len_freqs]
        self.explicit_rna_items = False

        self.dna = dna_type(dna)
        self.parser = DNA_parser(self.dna, self.freqs)
        self.item_starts = []
        self.rna = []
        self.cost = 0
        self.debug = False
        self.iteration = 0
class Executor(object):
    def __init__(self, dna):
        #assert all(c in 'ICFP' for c in dna)
        self.pattern_freqs = defaultdict(int)
        self.template_freqs = defaultdict(int)
        self.codon_len_freqs = defaultdict(int)
        self.freqs = [self.pattern_freqs, self.template_freqs, self.codon_len_freqs]
        self.explicit_rna_items = False

        self.dna = dna_type(dna)
        self.parser = DNA_parser(self.dna, self.freqs)
        self.item_starts = []
        self.rna = []
        self.cost = 0
        self.debug = False
        self.iteration = 0
             
    def obtain_rna(self):
        try:
            while True:
                self.step()
                for r in self.rna:
                    yield r
                self.rna = []
        except FinishException:
            pass
        for r in self.rna:
            yield r
        self.rna = []

    def step(self):
        if self.debug:
            print 'iteration', self.iteration
            print 'dna =', limit_string(self.dna)
            
        
        try:
            p = self.pattern()
            if self.debug:
                print 'pattern ', ''.join(map(str, p))
            t = self.template()
            if self.debug:
                print 'template', ''.join(map(str, t))
                
            for pp in p:
                self.pattern_freqs[type(pp)] += 1
            for tt in t:
                self.template_freqs[type(tt)] += 1
                
        finally:
            index = self.parser.index
            self.cost += index
            self.parser = None
            self.item_starts = []
            del self.dna[:index]
            
        self.matchreplace(p, t)
        self.parser = DNA_parser(self.dna, self.freqs)
        self.iteration += 1
        
        if self.debug:
            print 'len(rna) =', len(self.rna)
            print

    def pattern(self):
        result = []
        lvl = 0
        parser = self.parser
        while True:
            self.item_starts.append(parser.index)
            a = parser.read_codon()
            
            base = Base.decode.get(a)
            if base is not None:
                result.append(base)
                
            elif a == 'IP':
                result.append(Skip(self.nat()))
                
            elif a == 'IF':
                parser.read_base() # that's right
                result.append(Search(self.consts()))
                
            elif a == 'IIP':
                lvl += 1
                result.append(open_paren)
                
            elif a == 'IIC' or a == 'IIF':
                if lvl == 0:
                    return result
                lvl -= 1
                result.append(close_paren)
                
            elif a == 'III':
                command = ''
                for i in range(7):
                    command += parser.read_base()
                if self.explicit_rna_items:
                    result.append(RNA_Item(command))
                else:
                    self.rna.append(command)
                
            else:
                raise FinishException()
            
    def nat(self):
        result = 0
        power = 1
        while True:
            a = self.parser.read_base()
            if a == '' or a == 'P':
                break
            if a == 'C':
                result += power
            power *= 2
        
        return result    
    
    def consts(self):
        result = []
        while True:
            a = self.parser.read_codon()
            
            if a == 'C':
                result.append('I')
                
            elif a == 'F':
                result.append('C')
                
            elif a == 'P':
                result.append('F')
                
            elif a == 'IC':
                result.append('P')
                
            else:
                self.parser.unread_codon(a) #it will be later consumed in pattern()
                break
            
        return ''.join(result)
    
    def template(self):
        result = []
        parser = self.parser
        while True:
            self.item_starts.append(parser.index)
            a = parser.read_codon()
            
            base = Base.decode.get(a)
            if base is not None:
                result.append(base)
                
            elif a == 'IF' or a == 'IP':
                level = self.nat()
                n = self.nat()
                result.append(Reference(n, level))
                
            elif a == 'IIC' or a == 'IIF':
                return result
            
            elif a == 'IIP':
                result.append(Length(self.nat()))
            
            elif a == 'III':
                command = ''
                for i in range(7):
                    command += parser.read_base()
                if self.explicit_rna_items:
                    result.append(RNA_Item(command))
                else:
                    self.rna.append(command)
                
            else:
                raise FinishException(a)

            
    def matchreplace(self, pattern, template):
        e = []
        i = 0
        c = []
        dna = self.dna
        for p in pattern:
            tp = type(p)
            if tp is Base:
                self.cost += 1
                if dna[i] == p:
                    i += 1
                else:
                    if self.debug:
                        print 'failed match (base)'
                    return
                
            elif tp is Skip:
                i += p
                if i > len(dna):
                    if self.debug:
                        print 'failed match (skip)'
                    return
                
            elif tp is Search:
                j = next(kmp.find(dna, p, start=i), None)
                if j is not None:
                    self.cost += j + len(p) - i
                    i = j + len(p)
                else:
                    self.cost += len(dna) - i
                    if self.debug:
                        print 'failed match (search)'
                    return
                
            elif tp is OpenParen:
                c.append(i)
            elif tp is CloseParen:
                e.append((c.pop(), i))
            else:
                assert False, 'unknown pattern element'
        
        if self.debug:
            print 'succesful match of length', i
            for j, ee in enumerate(e):
                print 'e[{0}] = {1}'.format(j, limit_string(dna[ee[0]: ee[1]]))
        r = self.replacement(template, e)
        dna[:i] = r
        
    def replacement(self, template, e):
        r = dna_type()
        base_to_string = str.__str__
        for t in template:
            tt = type(t)
            if tt is Base:
                r.append(base_to_string(t))
            elif tt is Reference:
                if t.n < len(e):
                    begin, end = e[t.n]
                else:
                    begin, end = 0, 0
                if t.level == 0:
                    r.extend(self.dna[begin:end])
                else:
                    p = protect(self.dna[begin:end], t.level)
                    self.cost += len(p)
                    r.extend(p)
            elif tt is Length:
                if t < len(e):
                    begin, end = e[t]
                else:
                    begin, end = 0, 0
                r.extend(asnat(end - begin))
        return r