>>> list(Program("ls").gen_tokens())
        ['ls']
        >>> list(Program("ls -a").gen_tokens())
        ['ls', '-a']
        >>> list(Program("cd /; pwd").gen_tokens())
        ['cd', '/', None, 'pwd']
        >>> list(Program("'cd /; pwd'").gen_tokens())
        ['cd /; pwd']

def gen_tokens(self):
        """
        >>> list(Program("ls").gen_tokens())
        ['ls']
        >>> list(Program("ls -a").gen_tokens())
        ['ls', '-a']
        >>> list(Program("cd /; pwd").gen_tokens())
        ['cd', '/', None, 'pwd']
        >>> list(Program("'cd /; pwd'").gen_tokens())
        ['cd /; pwd']
        """
        current_token = []
        escape = False
        quote = None
        skip = 0
        for char, peek in zip(self.text, self.text[1:] + " "):
            if skip > 0:
                skip -= 1
                continue
            if quote is None:
                if escape:
                    current_token.append(char)
                    escape = False
                elif char in self.escape_chars:
                    escape = True
                elif char in QUOTES:
                    quote = char
                elif char in self.metacharacters:
                    if current_token:
                        yield token.Word(''.join(current_token))
                    current_token = []
                    if char == "(":
                        yield token.LParen()
                    elif char == ")":
                        yield token.RParen()
                    elif char in "|&;":
                        if peek == char:
                            yield token.Word(char + peek)
                            skip += 1
                        else:
                            yield token.Word(char)
                else:
                    current_token.append(char)
            elif char == quote:
                if current_token:
                    yield token.DoubleQuote(''.join(current_token))
                current_token = []
                quote = None
            else:
                current_token.append(char)
        if quote is not None:
            raise ValueError("No closing quotation")
        if escape:
            raise ValueError("No escaped character")
        if current_token:
            yield token.Word(''.join(current_token))