    //=============================================================================
    //  BWW to MusicXML converter
    //  Part of MusE Score
    //  Linux Music Score Editor
    //  $Id: parser.cpp 4873 2011-10-19 19:33:04Z lvinken $
    //
    //  Copyright (C) 2010 Werner Schweer and others
    //
    //  This program is free software; you can redistribute it and/or modify
    //  it under the terms of the GNU General Public License version 2.
    //
    //  This program is distributed in the hope that it will be useful,
    //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    //  GNU General Public License for more details.
    //
    //  You should have received a copy of the GNU General Public License
    //  along with this program; if not, write to the Free Software
    //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    //=============================================================================

    /**
     \file
     A simple parser for bww files. The file header is handled one line at a time,
     until a line starting with "&" is found. The parser then builds measures from
     uinterrupted sequences of notes.
     */

    // #include <iostream>

    #include <QtCore/QStringList>
    #include <QtCore/QtDebug>
    #include <QtCore/QMap>

    #include "lexer.h"
    #include "parser.h"
    #include "writer.h"

    // Duration of a whole measure in ticks
    static const int WHOLE_MEASURE_DURATION = 192;

    /**
     Determine if symbol is part of a note sequence
     */

    static bool isNote(Bww::Symbol sym)
    {
      return (sym == Bww::NOTE
              || sym == Bww::TIE
              || sym == Bww::TRIPLET
              || sym == Bww::GRACE);
    }

    /**
     Determine if symbol is part of a non-note sequence
     */

    static bool isNonNote(Bww::Symbol sym)
    {
      return (sym == Bww::CLEF
              || sym == Bww::KEY
              || sym == Bww::TSIG
              || sym == Bww::PART
              || sym == Bww::BAR);
    }

    /**
     Dump beams
     */

    static void dumpBeams(QList<Bww::MeasureDescription> const& measures)
    {
      for (int j = 0; j < measures.size(); ++j)
      {
        QString beams;
        QString beamStates;
        QVector<QString> beamList(3);
        for (int i = 0; i < measures.at(j).notes.size(); ++i)
        {
          QString beam = measures.at(j).notes.at(i).beam;
          if (beam == "")
            beam = " ";

          beams += beam;
          switch (measures.at(j).notes.at(i).beamState)
          {
          case Bww::ST_NONE:     beamStates += " "; break;
          case Bww::ST_START:    beamStates += "["; break;
          case Bww::ST_CONTINUE: beamStates += "_"; break;
          case Bww::ST_STOP:     beamStates += "]"; break;
          default:               beamStates += " ";
          }
          for (int k = 0; k < 3; k++)
            switch (measures.at(j).notes.at(i).beamList.at(k))
            {
            case Bww::BM_NONE:          beamList[k] += " "; break;
            case Bww::BM_BEGIN:         beamList[k] += "b"; break;
            case Bww::BM_CONTINUE:      beamList[k] += "c"; break;
            case Bww::BM_END:           beamList[k] += "e"; break;
            case Bww::BM_FORWARD_HOOK:  beamList[k] += ">"; break;
            case Bww::BM_BACKWARD_HOOK: beamList[k] += "<"; break;
            default:                    beamList[k] += "?";
            }
        }
        qDebug() << "beams measure #" << j + 1 << beams;
        qDebug() << "beams measure #" << j + 1 << beamStates;
        for (int k = 0; k < 3; k++)
          qDebug() << "beams measure #" << j + 1 << beamList.at(k);
      }
    }

    /**
     Dump measure contents
     */

    static void dumpMeasures(QList<Bww::MeasureDescription> const& measures)
    {
      qDebug() << "dumpMeasures #measures" << measures.size()
          ;
      for (int j = 0; j < measures.size(); ++j)
      {
        qDebug() << "measure #" << j + 1;
        qDebug() << "Measure contents:";
        qDebug() << "mbf:"
            << "repeatBegin" << measures.at(j).mbf.repeatBegin
            << "endingFirst" << measures.at(j).mbf.endingFirst
            << "endingSecond" << measures.at(j).mbf.endingSecond
            << "firstOfSystem" << measures.at(j).mbf.firstOfSystem
            << "irregular" << measures.at(j).mbf.irregular
            ;
        for (int i = 0; i < measures.at(j).notes.size(); ++i)
        {
          qDebug()
              << measures.at(j).notes.at(i).pitch
              << measures.at(j).notes.at(i).beam
              << measures.at(j).notes.at(i).type
              << measures.at(j).notes.at(i).dots
              << measures.at(j).notes.at(i).tieStart
              << measures.at(j).notes.at(i).tieStop
              << measures.at(j).notes.at(i).triplet
              << measures.at(j).notes.at(i).grace
              ;
        }
        qDebug() << "mef:"
            << "repeatEnd" << measures.at(j).mef.repeatEnd
            << "endingEnd" << measures.at(j).mef.endingEnd
            << "lastOfSystem" << measures.at(j).mef.lastOfSystem
            << "lastOfPart" << measures.at(j).mef.lastOfPart
            ;
        qDebug() << "duration:" << measures.at(j).duration;
      }
    }

    /**
     Calculate measure durations
     */

    static void calculateMeasureDurations(QList<Bww::MeasureDescription> & measures)
    {
      for (int j = 0; j < measures.size(); ++j)
      {
        int measureDuration = 0;
        for (int i = 0; i < measures.at(j).notes.size(); ++i)
        {
          int ticks = WHOLE_MEASURE_DURATION / measures.at(j).notes.at(i).type.toInt();
          if (measures.at(j).notes.at(i).dots) ticks = 3 * ticks / 2;
          if (measures.at(j).notes.at(i).triplet != Bww::ST_NONE) ticks = 2 * ticks / 3;
          if (measures.at(j).notes.at(i).grace) ticks = 0; // grace notes don't count
          measureDuration += ticks;
          qDebug()
              << measures.at(j).notes.at(i).pitch
              << measures.at(j).notes.at(i).beam
              << measures.at(j).notes.at(i).type
              << measures.at(j).notes.at(i).dots
              << measures.at(j).notes.at(i).tieStart
              << measures.at(j).notes.at(i).tieStop
              << measures.at(j).notes.at(i).triplet
              << measures.at(j).notes.at(i).grace
              << "->" << ticks
              ;
        }
        qDebug() << "measureDuration:" << measureDuration;
        measures[j].duration = measureDuration;
      }
    }

    /**
     Determine time signature
     */

    static void determineTimesig(QList<Bww::MeasureDescription> const& measures, int & beats, int & beat)
    {
      QMap<int, int> map;
      for (int j = 0; j < measures.size(); ++j)
      {
        int dur = measures[j].duration;
        if (map.contains(dur))
          map[dur]++;
        else
          map.insert(dur, 1);
      }
      // determine most common duration
      int commonDur = 0;
      int max = 0;
      QMap<int, int>::const_iterator i = map.constBegin();
      while (i != map.constEnd())
      {
        qDebug() << "measureDurations:" << i.key() << i.value() << endl;
        if (i.value() > max)
        {
          commonDur = i.key();
          max = i.value();
        }
        ++i;
      }
      qDebug() << "measureDuration commonDur:" << commonDur << "max:" << max;
      // determine time signature
      beat = 4;
      beats = 0;
      int divisor = WHOLE_MEASURE_DURATION / 4;
      for (; beat < 64; beat *= 2, divisor /= 2)
      {
        if ((commonDur % divisor) == 0)
        {
          beats = commonDur / divisor;
          qDebug()
              << "measureDuration found beat:" << beat
              << "beats:" << beats
              << "divisor:" << divisor
              ;
          return;
        }
      }
      // could not determine time signature, set default
      beat = 4;
      beats = 4;
    }

    /**
     Find irregular measures
     */

    static void findIrregularMeasures(QList<Bww::MeasureDescription> & measures, int beats, int beat)
    {
      qDebug() << "findIrregularMeasures" << measures.size()
          << "beats" << beats
          << "beat" << beat
          ;

      int normalDuration = WHOLE_MEASURE_DURATION * beats / beat;

      // need at least one measure
      if (measures.size() == 0) return;

      // if the first measure is shorter that normal, it is irregular
      if (measures.at(0).duration < normalDuration) measures[0].mbf.irregular = true;

      for (int j = 1; j < measures.size(); ++j)
      {
        // the second measure of a pair where the sum of their duration adds up
        // to the normal duration is also irregular
        const int d1 = measures.at(j - 1).duration;
        const int d2 = measures.at(j).duration;
        if (d1 > 0 && d2 > 0 && (d1 + d2) == normalDuration)
          measures[j].mbf.irregular = true;
      }
    }

    /**
     Set mef.lastOfPart flag on last measure
     */

    static void setLastOfPart(QList<Bww::MeasureDescription> & measures)
    {
      qDebug() << "dumpMeasures #measures" << measures.size()
          ;

      // need at least one measure
      if (measures.size() == 0) return;

      // set lastOfPart flag on last measure
      int j = measures.size() - 1;
      measures[j].mef.lastOfPart = true;
    }

    static QString findNextNextNoteBeam(QList<Bww::MeasureDescription> const& measures, int measureNr, int noteNr)
    {
      for (int i = noteNr + 1; i < measures.at(measureNr).notes.size(); ++i)
      {
        if (measures.at(measureNr).notes.at(i).grace)
          // ignore grace notes
          continue;
        return measures.at(measureNr).notes.at(i).beam;
      }
      return " "; // no next non-grace note found
    }

    static int type2beams(QString type)
    {
      if (type == "8") return 1;
      else if (type == "16") return 2;
      else if (type == "32") return 3;
      else return 0;
    }

    static void calculateHigherBeamStates(Bww::MeasureDescription & m)
    {
      qDebug() << "calculateHigherBeamStates";
      for (int i = 0; i < m.notes.size(); ++i)
      {
        qDebug()
            << m.notes.at(i).pitch
            << m.notes.at(i).beam
            << m.notes.at(i).type
            << m.notes.at(i).dots
            << m.notes.at(i).tieStart
            << m.notes.at(i).tieStop
            << m.notes.at(i).triplet
            << m.notes.at(i).grace
            ;

        if (m.notes.at(i).grace)
        {
          // just copy beamList[0] into beamList[1] and beamList[2]
          m.notes[i].beamList[1] = m.notes.at(i).beamList[0];
          m.notes[i].beamList[2] = m.notes.at(i).beamList[0];
        }
        else
        {
          int blp = -1; // beam level previous chord
          int blc = -1; // beam level current chord
          int bln = -1; // beam level next chord

          // find beam level current note
          blc = type2beams(m.notes.at(i).type);
          if (blc == 0)
            continue; // note does not have a beam

          // find beam level previous note
          if (m.notes.at(i).beamList[0] == Bww::BM_CONTINUE
              || m.notes.at(i).beamList[0] == Bww::BM_END)
          {
            for (int j = i - 1; blp == -1 && j >= 0; --j)
            {
              if (m.notes.at(j).grace)
                continue; // ignore grace notes
              blp = type2beams(m.notes.at(j).type);
            }
          }

          // find beam level next note
          if (m.notes.at(i).beamList[0] == Bww::BM_BEGIN
              || m.notes.at(i).beamList[0] == Bww::BM_CONTINUE)
          {
            for (int j = i + 1; bln == -1 && j < m.notes.size(); ++j)
            {
              if (m.notes.at(j).grace)
                continue; // ignore grace notes
              bln = type2beams(m.notes.at(j).type);
            }
          }

          qDebug()
              << "blp" << blp
              << "blc" << blc
              << "bln" << bln;
          for (int j = 2; j <= blc; ++j)
          {
            Bww::BeamType bt = Bww::BM_NONE;
            if (blp < j && bln >= j) bt = Bww::BM_BEGIN;
            else if (blp < j && bln < j) {
              if (bln > 0) bt = Bww::BM_FORWARD_HOOK;
              else if (blp > 0) bt = Bww::BM_BACKWARD_HOOK;
            }
            else if (blp >= j && bln < j) bt = Bww::BM_END;
            else if (blp >= j && bln >= j) bt = Bww::BM_CONTINUE;
            m.notes[i].beamList[j - 1] = bt;
            qDebug() << "beamList" << j - 1 << "=" << bt;
          }
        } // else
      } // for (int i = 0; i < m.notes.size(); ++i)
    }

    /**
     Determine all beam states.
     First for normal notes convert "r" and "l" in notes.beam into BM_BEGIN,
     BM_CONTINUE and BM_END in notes.beamList[0]. For grace notes "b", "c" and
     "e" can be directly converted into the corresponding beam states in
     notes.beamList[0].
     Then calculate the higher level beams.
     */

    static void determineBeamStates(QList<Bww::MeasureDescription> & measures)
    {
      enum State { NONE, LEFT, RIGHT };
      for (int j = 0; j < measures.size(); ++j)
      {
        State state = NONE;
        for (int i = 0; i < measures.at(j).notes.size(); ++i)
        {
          QString beam = measures.at(j).notes.at(i).beam;
          // handle normal notes
          if (beam == "")
          {
            measures[j].notes[i].beamState = Bww::ST_NONE;
            measures[j].notes[i].beamList[0] = Bww::BM_NONE;
            state = NONE;
          }
          else if (beam == "r")
          {
            if (state == NONE)
            {
              measures[j].notes[i].beamState = Bww::ST_START;
              measures[j].notes[i].beamList[0] = Bww::BM_BEGIN;
              state = LEFT; // now in left part of beam
            }
            else if (state == LEFT)
            {
              measures[j].notes[i].beamState = Bww::ST_CONTINUE;
              measures[j].notes[i].beamList[0] = Bww::BM_CONTINUE;
            }
            else if (state == RIGHT)
            {
              // shouldn't happen TODO report (internal?) error
            }
          }
          else if (beam == "l")
          {
            if (state == NONE)
            {
              // shouldn't happen TODO report error
            }
            else if (state == LEFT || state == RIGHT)
            {
              // if the beam does not end here (next note has beam "l")
              // then beamState is CONTINUE else STOP
              if (findNextNextNoteBeam(measures, j, i) == "l")
              {
                measures[j].notes[i].beamState = Bww::ST_CONTINUE;
                measures[j].notes[i].beamList[0] = Bww::BM_CONTINUE;
                state = RIGHT; // now in right part of beam
              }
              else
              {
                measures[j].notes[i].beamState = Bww::ST_STOP;
                measures[j].notes[i].beamList[0] = Bww::BM_END;
                state = NONE; // now in right part of beam
              }
            }
          }
          // handle grace notes
          else if (beam == "b") measures[j].notes[i].beamList[0] = Bww::BM_BEGIN;
          else if (beam == "c") measures[j].notes[i].beamList[0] = Bww::BM_CONTINUE;
          else if (beam == "e") measures[j].notes[i].beamList[0] = Bww::BM_END;
        }
        calculateHigherBeamStates(measures[j]);
      }
    }

    namespace Bww {

      /**
       Parser constructor, using Lexer \a l and Writer \a w.
       */

      Parser::Parser(Lexer& l, Writer& w)
        : lex(l),
        wrt(w),
        tempo(0),
        //inMeasure(false),
        //measureNr(0),
        tieStart(false),
        inTie(false),
        tripletStart(false),
        inTriplet(false),
        tsigFound(false)
      {
        qDebug() << "Parser::Parser()";
      }

      /**
       Parse the input stream and write result.
       */

      void Parser::parse()
      {
        // read the header, handling only strings and tune tempo
        while (lex.symType() == COMMENT
               || lex.symType() == STRING
               || lex.symType() == TEMPO)
        {
          if (lex.symType() == STRING)
            parseString();
          else if (lex.symType() == TEMPO)
            parseTempo();
          else if (lex.symType() == COMMENT)
            lex.getSym();
        }
        qDebug() << "Parser::parse()"
            << "title:" << title
            << "type:" << type
            << "composer:" << composer
            << "footer:" << footer
            ;
        wrt.header(title, type, composer, footer, tempo);

        // read the actual music
        if (lex.symType() != CLEF)
          errorHandler("clef ('&') expected");
        while (lex.symType() != NONE)
        {
          if (isNonNote(lex.symType()))
            parseSeqNonNotes();
          else if (isNote(lex.symType()))
            parseSeqNotes();
          else if (lex.symType() == UNKNOWN)
          {
            errorHandler("unknown symbol '" + lex.symValue() + "'");
            lex.getSym();
          }
          else
          {
            ; // others not implemented yet: silently ignored
            lex.getSym();
          }
        }

        qDebug() << "Parser::parse() finished, #measures" << measures.size()
            ;

        calculateMeasureDurations(measures);
        if (!tsigFound)
        {
          determineTimesig(measures, beats, beat);
          wrt.tsig(beats, beat);
        }
        findIrregularMeasures(measures, beats, beat);
        determineBeamStates(measures);
        setLastOfPart(measures);
        dumpMeasures(measures);
        dumpBeams(measures);

        for (int j = 0; j < measures.size(); ++j)
        {
          wrt.beginMeasure(measures.at(j).mbf);
          for (int i = 0; i < measures.at(j).notes.size(); ++i)
          {
            wrt.note(measures.at(j).notes.at(i).pitch,
                     measures.at(j).notes.at(i).beamList,
                     measures.at(j).notes.at(i).type,
                     measures.at(j).notes.at(i).dots,
                     measures.at(j).notes.at(i).tieStart,
                     measures.at(j).notes.at(i).tieStop,
                     measures.at(j).notes.at(i).triplet,
                     measures.at(j).notes.at(i).grace
                     );
          }
          wrt.endMeasure(measures.at(j).mef);
        }

        // trailer
        wrt.trailer();
      }

      /**
       Display error \a s.
       */

      void Parser::errorHandler(QString /*s*/)
      {
    #if 0 // WS
        std::cerr << "Parse error line "
            << lex.symLineNumber() + 1
            << ": "
            << qPrintable(s)
            << std::endl;
    #endif
      }

      /**
       Parse a bww bar symbol.
       */

      void Parser::parseBar(Bww::MeasureEndFlags& mef)
      {
        qDebug() << "Parser::parseBar() value:" << qPrintable(lex.symValue());
        if (lex.symValue() == "!!t") mef.doubleBarLine = true;
        lex.getSym();
      }

      /**
       Parse a bww note.
       */

      void Parser::parseNote()
      {
        qDebug() << "Parser::parseNote() value:" << qPrintable(lex.symValue());

        QRegExp rNotes("(LG|LA|[B-F]|HG|HA)([lr]?)_(1|2|4|8|16|32)");

        QStringList caps;
        if (rNotes.exactMatch(lex.symValue()))
        {
          caps = rNotes.capturedTexts();
          qDebug() << " match" << caps.size();
          if (caps.size() == 4)
          {
            qDebug()
                << "caps[1]" << caps.at(1)
                << "caps[2]" << caps.at(2)
                << "caps[3]" << caps.at(3)
                ;
          }
        }
        lex.getSym();

        int dots = 0;
        bool tieStop = false;
        if (tieStart) inTie = true;
        bool tripletStop = false;
        if (tripletStart) inTriplet = true;
        if (lex.symType() == DOT)
        {
          qDebug() << " dot" << qPrintable(lex.symValue());
          ++dots;
          lex.getSym();
        }
        else if (lex.symType() == TIE)
        {
          qDebug() << " tie" << qPrintable(lex.symValue());
          if (lex.symValue() == "^ts")
          {
            if (inTie) errorHandler("tie start ('^ts') unexpected");
          }
          else
          {
            if (!inTie) errorHandler("tie end ('^te') unexpected");
            else
            {
              tieStop = true;
              inTie = false;
              lex.getSym();
            }
          }
        }
        else if (lex.symType() == TRIPLET)
        {
          qDebug() << " triplet" << qPrintable(lex.symValue());
          if (lex.symValue() == "^3s")
          {
            if (inTriplet) errorHandler("triplet start ('^3s') unexpected");
          }
          else
          {
            if (!inTriplet) errorHandler("triplet end ('^3e') unexpected");
            else
            {
              tripletStop = true;
            }
          }
          lex.getSym();
        }

        StartStop triplet = ST_NONE;
        if (inTriplet)
        {
          if (tripletStart) triplet = ST_START;
          else if (tripletStop) triplet = ST_STOP;
          else triplet = ST_CONTINUE;
        }
        qDebug() << " tie start" << tieStart << " tie stop" << tieStop;
        qDebug() << " triplet start" << tripletStart << " triplet stop" << tripletStop;
        NoteDescription noteDesc(caps[1], caps[2], caps[3], dots, tieStart, tieStop, triplet);

        if (measures.isEmpty())
        {
          errorHandler("cannot append note: no measure");
        }
        else
        {
          measures.last().notes.append(noteDesc);
        }
        tieStart = false;
        tripletStart = false;
        if (tripletStop)
        {
          inTriplet = false;
          tripletStop = false;
        }
      }

      /**
       Determine beam for grace note \a index in a group of \a size grace notes.
       */

      static QString graceBeam(const int size, const int index)
      {
        if (size <= 1) return " ";                // no beam
        if (index < 0) return " ";                // no beam (but should not happen)
        if (index == 0) return "b";               // begin
        else if (index == (size - 1)) return "e"; // end
        else if (index >= size) return " ";       // no beam (but should not happen)
        else return "c";                          // continue
      }

      /**
       Parse a bww embellishment.
       */

      void Parser::parseGraces()
      {
        qDebug() << "Parser::parseGraces() value:" << qPrintable(lex.symValue());

        const QString type = "32";
        const int dots = 0;
        QStringList graces = lex.symValue().split(" ");
        for (int i = 0; i < graces.size(); ++i)
        {
          const QString beam = graceBeam(graces.size(), i);
          NoteDescription noteDesc(graces.at(i), beam, type, dots, false, false, ST_NONE, true);
          if (measures.isEmpty())
          {
            errorHandler("cannot append note: no measure");
          }
          else
          {
            measures.last().notes.append(noteDesc);
          }
        }
        lex.getSym();
      }

      /**
       Parse a bww part symbol.
       */

      void Parser::parsePart(Bww::MeasureBeginFlags& mbf, Bww::MeasureEndFlags& mef)
      {
        qDebug() << "Parser::parsePart() value:" << qPrintable(lex.symValue());
        if (lex.symValue() == "I!''")
        {
          mbf.repeatBegin = true;
        }
        else if (lex.symValue() == "'1")
        {
          mbf.endingFirst = true;
        }
        else if (lex.symValue() == "'2")
        {
          mbf.endingSecond = true;
        }
        else if (lex.symValue() == "''!I")
        {
          mef.repeatEnd = true;
        }
        else if (lex.symValue() == "_'")
        {
          mef.endingEnd = true;
        }
        else
        {
          ; // other silently ignored
        }
        lex.getSym();
      }

      /**
       Parse a sequence of non-notes.
       */

      void Parser::parseSeqNonNotes()
      {
        qDebug() << "Parser::parseSeqNonNotes() value:" << qPrintable(lex.symValue());
        MeasureBeginFlags mbfl;
        MeasureEndFlags mefl;
        while (isNonNote(lex.symType()))
        {
          if (lex.symType() == CLEF)
          {
            mbfl.firstOfSystem = true;
            mefl.lastOfSystem = true;
            lex.getSym();
          }
          else if (lex.symType() == KEY)
            lex.getSym(); // ignore
          else if (lex.symType() == TSIG)
            parseTSig();
          else if (lex.symType() == PART)
            parsePart(mbfl, mefl);
          else if (lex.symType() == BAR)
            parseBar(mefl);
        }
        // First end the previous measure
        if (!measures.isEmpty())
        {
          measures.last().mef = mefl;
        }
        // Then start a new measure, if necessary
        if (isNote(lex.symType()))
        {
          MeasureDescription md;
          md.mbf = mbfl;
          measures.append(md);
        }
      }

      /**
       Parse a sequence of notes.
       Includes handling ties and triplets, but without extensive error checking.
       May break on invalid input.
       */

      void Parser::parseSeqNotes()
      {
        qDebug() << "Parser::parseSeqNotes() value:" << qPrintable(lex.symValue());
        while (lex.symType() == GRACE || lex.symType() == NOTE || lex.symType() == TIE || lex.symType() == TRIPLET)
        {
          if (lex.symType() == GRACE) parseGraces();
          else if (lex.symType() == NOTE) parseNote();
          else if (lex.symType() == TIE)
          {
            if (lex.symValue() == "^ts")
            {
              if (inTie) errorHandler("tie start ('^ts') unexpected");
              else tieStart = true;
            }
            else
            {
              errorHandler("tie end ('^te') unexpected");
            }
            lex.getSym();
          }
          else if (lex.symType() == TRIPLET)
          {
            if (lex.symValue() == "^3s")
            {
              if (inTriplet) errorHandler("triplet start ('^3s') unexpected");
              else tripletStart = true;
            }
            else
            {
              errorHandler("triplet end ('^3e') unexpected");
            }
            lex.getSym();
          }
        }
      }

      /**
       Parse a bww string. Extract text and type.
       Example: "Air",(Y,C,0,0,Times New Roman,14,400,0,0,18,0,0,0)
       */

      void Parser::parseString()
      {
        qDebug() << "Parser::parseString() value:" << qPrintable(lex.symValue());

        QRegExp rString("\\\"(.*)\\\",\\(([A-Z]),.*\\)");

        if (rString.exactMatch(lex.symValue()))
        {
          QStringList caps = rString.capturedTexts();
          if (caps.size() == 3)
          {
            if (caps.at(2) == "T") title = caps.at(1);
            if (caps.at(2) == "Y") type = caps.at(1);
            if (caps.at(2) == "M") composer = caps.at(1);
            if (caps.at(2) == "F") footer = caps.at(1);
          }
        }
        lex.getSym();
      }

      /**
       Parse a bww tempo symbol.
       */

      void Parser::parseTempo()
      {
        qDebug() << "Parser::parseTempo() value:" << qPrintable(lex.symValue());

        QRegExp rTempo("^TuneTempo,(\\d+)");

        if (rTempo.exactMatch(lex.symValue()))
        {
          QStringList caps = rTempo.capturedTexts();
          if (caps.size() == 2)
          {
            tempo = caps.at(1).toInt();
          }
        }
        lex.getSym();
      }
      /**
       Parse a bww tsig symbol.
       */

      void Parser::parseTSig()
      {
        qDebug() << "Parser::parseTSig() value:" << qPrintable(lex.symValue());

        QRegExp rTSig("(\\d+)_(1|2|4|8|16|32)");

        if (rTSig.exactMatch(lex.symValue()))
        {
          QStringList caps = rTSig.capturedTexts();
          if (caps.size() == 3)
          {
            beats = caps.at(1).toInt();
            beat  = caps.at(2).toInt();
            tsigFound = true;
            wrt.tsig(beats, beat);
          }
        }
        lex.getSym();
      }

    } // namespace Bww

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: beat, beats.