// Close the input file and throw an ExceptionParseError exception.
    void parse_error()
    {
        const size_t line_number = m_lexer.get_line_number();

        m_lexer.close();

        throw ExceptionParseError(line_number);
    }
    void parse_f_statement()
    {
        clear_keep_memory(m_face_vertex_indices);
        clear_keep_memory(m_face_tex_coord_indices);
        clear_keep_memory(m_face_normal_indices);

        while (true)
        {
            m_lexer.eat_blanks();

            if (m_lexer.is_eol())
                break;

            //
            // Recognized (epsilon)
            // Accept n
            //

            {
                const long n = m_lexer.accept_long();
                const size_t v = fix_index(n, m_vertices.size());
                m_face_vertex_indices.push_back(v);
            }

            //
            // Recognized n
            // Accept (epsilon), /
            //

            {
                const unsigned char c = m_lexer.get_char();
                if (m_lexer.is_space(c))
                    continue;
                else if (c == '/')
                    m_lexer.next_char();
                else parse_error();
            }

            //
            // Recognized n/
            // Accept /, n
            //

            {
                const unsigned char c = m_lexer.get_char();
                if (c == '/')
                {
                    m_lexer.next_char();
                    goto skip;
                }
                else
                {
                    const long n = m_lexer.accept_long();
                    const size_t vt = fix_index(n, m_tex_coords.size());
                    m_face_tex_coord_indices.push_back(vt);
                }
            }

            //
            // Recognized n/n
            // Accept (epsilon), /
            //

            {
                const unsigned char c = m_lexer.get_char();
                if (m_lexer.is_space(c))
                    continue;
                else if (c == '/')
                    m_lexer.next_char();
                else parse_error();
            }

          skip:

            //
            // Recognized n//, n/n/
            // Accept (epsilon), n
            //

            {
                const unsigned char c = m_lexer.get_char();
                if (m_lexer.is_space(c))
                    continue;
                else
                {
                    const long n = m_lexer.accept_long();
                    const size_t vn = fix_index(n, m_normals.size());
                    m_face_normal_indices.push_back(vn);
                }
            }
        }

        // Check whether the face is well-formed.
        const size_t vc = m_face_vertex_indices.size();
        const size_t tc = m_face_tex_coord_indices.size();
        const size_t nc = m_face_normal_indices.size();
        const bool well_formed =
                vc >= 3
            && (tc == 0 || tc == vc)
            && (nc == 0 || nc == vc);

        if (well_formed)
        {
            // The face is well-formed, insert it into the mesh.
            insert_face_into_mesh();
        }
        else
        {
            // The face is ill-formed, ignore it or abort parsing.
            if (m_options & StopOnInvalidFaceDef)
                throw ExceptionInvalidFaceDef(m_lexer.get_line_number());
        }
    }