diff --git a/README.md b/README.md index 45ff1ba..0b85468 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This code is being developed using Qt5, with development work being done on Linu Among other things, the app includes: * Code that reads and parses DOS-format disk images at the disk-structure level. (It's just read-only for now, and ProDOS is on the horizon) -* An Applesoft parser/retokenizer/reformatter which can show code in its normal form or reindented on multiple lines, and can optionally show integers as hex values inline. The retokenizer creates new tokens for strings, variable names, numeric values, DATA payloads, REM remarks, and other things that are nice to have -- and sets the stage to create an analyzer for deeper processing of the code later on. +* An Applesoft parser/retokenizer/reformatter which can show code in its normal form or reindented on multiple lines with syntax highlighting, and can optionally show integers as hex values inline. The retokenizer creates new tokens for strings, variable names, numeric values, DATA payloads, REM remarks, and other things that are nice to have -- and sets the stage to create an analyzer for deeper processing of the code later on. * A binary file processor with a flow-tracing disassembler, which can show hex dumps and disassembly of code and data blocks (it also temporarily has some common labels for well-known addresses hard-coded into the display, though dynamic labeling of symbols is in the works.) It can also display the relocation table for type-R relocatable files. * A Text file viewer. * A graphics viewer which can show hires screen dumps as monochrome, with NTSC artifacts, or a hybrid mode where each pixel's natural color is shown in-place (with no white color blending.) @@ -20,7 +20,7 @@ Among other things, the app includes: There is also a Hex/Decimal converter utility and some in-app documentation for HRCG control character codes. Mainly stuff that I found useful at the time I was writing it. In the pipeline are some more useful features (as I get a chance): -* Syntax Highlighting +* Syntax Highlighting **(Currently in progress!)** * More low-level disk exploration tools (sector-level stuff, comparing DOS versions between disks, etc.) * More analysis of AppleSoft programs (Tracing flow control, find addresses of PEEKS, POKES, CALLS, USRs, and &'s) * Processing and analysis of Integer Basic files diff --git a/doc/AppleSAWS.notes.odt b/doc/AppleSAWS.notes.odt index 92b36fc..f3e82e5 100644 Binary files a/doc/AppleSAWS.notes.odt and b/doc/AppleSAWS.notes.odt differ diff --git a/src/applesoftfile/ApplesoftRetokenizer.cpp b/src/applesoftfile/ApplesoftRetokenizer.cpp index 9599385..ce75e0b 100644 --- a/src/applesoftfile/ApplesoftRetokenizer.cpp +++ b/src/applesoftfile/ApplesoftRetokenizer.cpp @@ -6,17 +6,25 @@ ApplesoftRetokenizer::ApplesoftRetokenizer() { - + m_isParsed = false; } void ApplesoftRetokenizer::setData(QByteArray data) { m_data = data; m_data_end = data.length(); + m_isParsed = false; } void ApplesoftRetokenizer::parse(quint16 start_address) { + if (m_isParsed) + { + qWarning("File is already parsed. Not reparsing."); + return; + } + + //TODO: This could be changed to search for hidden space between applesoft lines int idx = 0; quint8 val = 0; m_retokenized_lines.clear(); @@ -46,11 +54,123 @@ void ApplesoftRetokenizer::parse(quint16 start_address) m_retokenized_lines.append(line); } - m_data_end = idx; + m_data_end = idx; if (idx < m_data.length()) { qDebug() << QString("%1 byte(s) unaccounted for.").arg(m_data.length() - idx); } + + retokenizeLinesForFormatting(); + + m_isParsed = true; +} + +void ApplesoftRetokenizer::retokenizeLinesForFormatting() +{ + QVector retLines; + + foreach(ApplesoftLine line, m_retokenized_lines) + { + int indentlevel = 1; + // quint16 linenum = line.linenum; + + bool firstToken = true; + ApplesoftToken previousToken; + QMutableVectorIterator tokenIt(line.tokens); + while (tokenIt.hasNext()) + { + ApplesoftToken token = tokenIt.next(); + bool isFlowTarget = false; + + QString tokenstr = token.getRawPrintableString(); + if (firstToken) + { + if (!tokenstr.startsWith(" ")) + { + ApplesoftToken tmptoken(ApplesoftToken::OptFmtLeadingSpaceTokenValue); + tokenIt.remove(); + tokenIt.insert(tmptoken); + tokenIt.insert(token); + } + firstToken = false; + } + + quint16 preTokenId = previousToken.getTokenId(); + if (preTokenId == ApplesoftToken::ASGoto || + preTokenId == ApplesoftToken::ASGosub || + preTokenId == ApplesoftToken::ASThen) + { + isFlowTarget = false; + if (preTokenId == ApplesoftToken::ASGoto || preTokenId == ApplesoftToken::ASGosub) + { + isFlowTarget = true; + } + else if (preTokenId == ApplesoftToken::ASThen + && token.getTokenId() == ApplesoftToken::IntegerTokenVal) + { + isFlowTarget = true; + } + if (isFlowTarget) + { + QPair pair; + pair.first = line.linenum; + pair.second = token.getWordValue(); + m_flowTargets.append(pair); + + ApplesoftToken tmptoken(ApplesoftToken::OptFmtFlagFlowTargetNextTokenValue); + tokenIt.remove(); + tokenIt.insert(tmptoken); + tokenIt.insert(token); + } + } + + if (token.getTokenId() == ApplesoftToken::ASReturn) + { + ApplesoftToken tmptoken(ApplesoftToken::OptFmtReturnLineBreakTokenValue); + tokenIt.insert(tmptoken); + } + + if (token.getTokenId() == ':') + { + ApplesoftToken tmptoken(ApplesoftToken::OptFmtIndentLineBreakTokenValue); + tokenIt.insert(tmptoken); + for (int ind = 0; ind < indentlevel; ind++) + { + ApplesoftToken tmptoken(ApplesoftToken::OptFmtIndentTabTokenValue); + tokenIt.insert(tmptoken); + } + if (!tokenIt.peekNext().getRawPrintableString().startsWith(" ")) + { + ApplesoftToken tmptoken(ApplesoftToken::OptFmtIndentSpaceTokenValue); + tokenIt.insert(tmptoken); + } + } + if (token.getTokenId() == ApplesoftToken::ASThen) + { + indentlevel++; + if (tokenIt.peekNext().getTokenId() != ApplesoftToken::IntegerTokenVal) + { + ApplesoftToken tmptoken(ApplesoftToken::OptFmtIndentLineBreakTokenValue); + tokenIt.insert(tmptoken); + for (int ind = 0; ind < indentlevel; ind++) + { + ApplesoftToken tmptoken(ApplesoftToken::OptFmtIndentTabTokenValue); + tokenIt.insert(tmptoken); + } + if (!tokenIt.peekNext().getRawPrintableString().startsWith(" ")) + { + ApplesoftToken tmptoken(ApplesoftToken::OptFmtIndentSpaceTokenValue); + tokenIt.insert(tmptoken); + } + } + } + + previousToken = token; + } + retLines.append(line); + } + + m_retokenized_lines = retLines; } void ApplesoftRetokenizer::retokenizeLine(ApplesoftLine &line) @@ -115,35 +235,35 @@ QVector ApplesoftRetokenizer::retokenizeStrings(QVector= 0x80) { replacements.append(token); - // continue; + // continue; } else - if (token.getWordValue() == '"') - { - if (!inString) + if (token.getWordValue() == '"') { - inString = true; - buffer.append(token.getWordValue()); - // continue; - } - else - { - buffer.append(token.getWordValue()); - ApplesoftToken strtoken(ApplesoftToken::StringTokenVal, buffer); - replacements.append(strtoken); - buffer.clear(); - inString = false; -// continue; - } - } else + if (!inString) + { + inString = true; + buffer.append(token.getWordValue()); + // continue; + } + else + { + buffer.append(token.getWordValue()); + ApplesoftToken strtoken(ApplesoftToken::StringTokenVal, buffer); + replacements.append(strtoken); + buffer.clear(); + inString = false; + // continue; + } + } else - if (inString) - { - buffer.append(token.getWordValue()); - // continue; - } else + if (inString) + { + buffer.append(token.getWordValue()); + // continue; + } else - replacements.append(token); + replacements.append(token); } return replacements; @@ -176,7 +296,7 @@ QVector ApplesoftRetokenizer::retokenizeDataStatements(QVector dataTokens; - dataTokens = processDataPayload(datatokenbuffer); + dataTokens = retokenizeDataPayload(datatokenbuffer); replacements.append(dataTokens); datatokenbuffer.clear(); inData = false; @@ -185,7 +305,7 @@ QVector ApplesoftRetokenizer::retokenizeDataStatements(QVector ApplesoftRetokenizer::processDataPayload(QVector& datatokens) +QVector ApplesoftRetokenizer::retokenizeDataPayload(QVector& datatokens) { QVector retval; @@ -249,13 +369,13 @@ QVector ApplesoftRetokenizer::retokenizeVariables(QVector matchstack; QRegularExpressionMatchIterator matches = varregexp.globalMatch(parsestring); - // qDebug() << parsestring; + // qDebug() << parsestring; while (matches.hasNext()) { QRegularExpressionMatch rematch = matches.next(); matchstack.push_front(rematch); -// qDebug() << "Capture " << " = " << rematch.capturedTexts() << "From: " << rematch.capturedStart() -// << "To: " << rematch.capturedEnd()-1 << "("< ApplesoftRetokenizer::retokenizeNumbers(QVector matchstack; QRegularExpressionMatchIterator matches = varregexp.globalMatch(parsestring); -// qDebug() << parsestring; + // qDebug() << parsestring; while (matches.hasNext()) { QRegularExpressionMatch rematch = matches.next(); matchstack.push_front(rematch); -// qDebug() << "Capture " << " = " << rematch.capturedTexts() << "From: " << rematch.capturedStart() -// << "To: " << rematch.capturedEnd()-1 << "("< ApplesoftRetokenizer::retokenizeNegativeNumbers(QVector< else if (token.getTokenId() == ApplesoftToken::IntVarTokenVal) lastWasInt = true; else if (token.getTokenId() == ApplesoftToken::FloatVarTokenVal) lastWasInt = true; else if (token.getTokenId() == ')') lastWasInt = true; -else - if (token.getTokenId() == ApplesoftToken::ASMINUS) - { - if (!lastWasInt && it.hasNext() && it.peekNext().getTokenId() == ApplesoftToken::IntegerTokenVal) + else + if (token.getTokenId() == ApplesoftToken::ASMINUS) { - it.remove(); - token = it.next(); - it.remove(); - int val = token.getUnsignedIntegerValue() * -1; - token.setValue(val); - it.insert(token); - lastWasInt = true; + if (!lastWasInt && it.hasNext() && it.peekNext().getTokenId() == ApplesoftToken::IntegerTokenVal) + { + it.remove(); + token = it.next(); + it.remove(); + int val = token.getUnsignedIntegerValue() * -1; + token.setValue(val); + it.insert(token); + lastWasInt = true; + } + else + { + lastWasInt = false; + } } else { lastWasInt = false; } - } - else - { - lastWasInt = false; - } } return tmptokens.toVector(); diff --git a/src/applesoftfile/ApplesoftRetokenizer.h b/src/applesoftfile/ApplesoftRetokenizer.h index d1d9913..cedc0b6 100644 --- a/src/applesoftfile/ApplesoftRetokenizer.h +++ b/src/applesoftfile/ApplesoftRetokenizer.h @@ -5,6 +5,8 @@ #include #include +#include +#include class ApplesoftRetokenizer { @@ -18,8 +20,12 @@ public: QVector getRetokenizedLines() { return m_retokenized_lines; } + QList > getFlowTargets() const { return m_flowTargets; } private: + void retokenizeLinesForFormatting(); + + void retokenizeLine(ApplesoftLine &line); QVector retokenizeRems(QVector &datatokens); QVector retokenizeStrings(QVector &datatokens); @@ -29,10 +35,16 @@ private: QVector retokenizeNumbers(QVector &datatokens); QVector retokenizeNegativeNumbers(QVector &datatokens); + QVector m_retokenized_lines; QByteArray m_data; quint16 m_data_end; + + bool m_isParsed; + + QList > m_flowTargets; + }; diff --git a/src/applesoftfile/applesoftfile.cxx b/src/applesoftfile/applesoftfile.cxx index 37f029b..53557b9 100644 --- a/src/applesoftfile/applesoftfile.cxx +++ b/src/applesoftfile/applesoftfile.cxx @@ -35,6 +35,8 @@ void ApplesoftFile::setData(QByteArray data) m_retokenizer->parse(); m_data_end = m_retokenizer->getEndOfDataOffset(); m_lines = m_retokenizer->getRetokenizedLines(); + + m_flowTargets = m_retokenizer->getFlowTargets(); } QByteArray ApplesoftFile::rawData() { diff --git a/src/applesoftfile/applesoftfile.h b/src/applesoftfile/applesoftfile.h index 7c5449f..d08fba3 100644 --- a/src/applesoftfile/applesoftfile.h +++ b/src/applesoftfile/applesoftfile.h @@ -33,6 +33,9 @@ private: quint16 m_length; ApplesoftRetokenizer *m_retokenizer; + + QList > m_flowTargets; + }; diff --git a/src/applesoftfile/applesoftformatter.cxx b/src/applesoftfile/applesoftformatter.cxx index 8ad69da..61fecab 100644 --- a/src/applesoftfile/applesoftformatter.cxx +++ b/src/applesoftfile/applesoftformatter.cxx @@ -9,7 +9,6 @@ ApplesoftFormatter::ApplesoftFormatter(QObject *parent) : QObject(parent) { m_file = Q_NULLPTR; - m_formattedDocument = QSharedPointer(new QTextDocument()); } void ApplesoftFormatter::setFile(ApplesoftFile *file) @@ -21,13 +20,11 @@ void ApplesoftFormatter::setFile(ApplesoftFile *file) void ApplesoftFormatter::formatText() { m_formattedText.clear(); - m_formattedDocument->clear(); if (!m_file) { return; } - QTextCursor cursor(m_formattedDocument.data()); QString formattedText; m_flowTargets.clear(); @@ -37,7 +34,6 @@ void ApplesoftFormatter::formatText() int indentlevel = 1; formattedText.append(linestring); - cursor.insertText(linestring, ApplesoftToken::defaultTextFormat()); QVectorIterator tokenIt(line.tokens); @@ -58,7 +54,6 @@ void ApplesoftFormatter::formatText() } firstToken = false; } - cursor.insertText(tokenstr, token.textFormat()); //TODO: Move this to the parser. @@ -69,12 +64,12 @@ void ApplesoftFormatter::formatText() { isFlowTarget = false; if (previousToken.getTokenId() == ApplesoftToken::ASGoto || - previousToken.getTokenId() == ApplesoftToken::ASGosub) + previousToken.getTokenId() == ApplesoftToken::ASGosub) { isFlowTarget = true; } else if (previousToken.getTokenId() == ApplesoftToken::ASThen && - token.getTokenId() == ApplesoftToken::IntegerTokenVal) + token.getTokenId() == ApplesoftToken::IntegerTokenVal) { isFlowTarget = true; } @@ -177,14 +172,244 @@ void ApplesoftFormatter::formatText() formattedText.append(tokenstr); previousToken = token; - } + } // While tokenIt.hasNext(); formattedText.append("\n"); if (m_format_options.testFlag(ReindentCode)) { - // retval.append("\n"); + // retval.append("\n"); } - } + } //foreach line m_formattedText = formattedText; } + + +void ApplesoftFormatter::newFormatText() +{ + m_formattedText.clear(); + + QString formattedText; + + foreach (ApplesoftLine line, m_file->getLines()) + { + QString linestring = QString("%1 ").arg(line.linenum,5,10,QChar(' ')); + formattedText.append(linestring); + + QVectorIteratortokenIt(line.tokens); + while (tokenIt.hasNext()) + { + ApplesoftToken token = tokenIt.next(); + bool isBranchTarget = false; + if (token.isOptFmtToken()) + { + switch (token.getTokenId()) + { + + case ApplesoftToken::OptFmtFlagFlowTargetNextTokenValue: + { + if (m_format_options.testFlag(ShowIntsAsHex)) + isBranchTarget = true; + break; + } + + case ApplesoftToken::OptFmtIndentLineBreakTokenValue: + { + if (m_format_options.testFlag(ReindentCode)) + formattedText.append("\n"); + break; + } + case ApplesoftToken::OptFmtIndentSpaceTokenValue: + { + if (m_format_options.testFlag(ReindentCode)) + formattedText.append(" "); + break; + } + case ApplesoftToken::OptFmtIndentTabTokenValue: + { + if (m_format_options.testFlag(ReindentCode)) + formattedText.append(" "); + break; + } + case ApplesoftToken::OptFmtLeadingSpaceTokenValue: + { + formattedText.append(" "); + break; + } + case ApplesoftToken::OptFmtReturnLineBreakTokenValue: + { + if (m_format_options.testFlag(BreakAfterReturn)) + formattedText.append("\n"); + break; + } + default: + { + break; + } + } + } // isOptFmt + else + { + QString tokenstr = token.getRawPrintableString(); + + if (token.getTokenId() == ApplesoftToken::IntegerTokenVal) + { + if (m_format_options.testFlag(ShowIntsAsHex) && !isBranchTarget) + { + quint32 ui32val = token.getUnsignedIntegerValue(); + qint32 i32val = token.getIntegerValue(); + if ((i32val < 128 && i32val >= -128) || ui32val < 256) + { + quint8 ui8 = ui32val; + tokenstr = HEXPREFIX+uint8ToHex(ui8); + } + else if ((i32val < 32768 && i32val >= -32768) || ui32val < 65536) + { + quint16 ui16 = ui32val; + tokenstr = HEXPREFIX+uint16ToHex(ui16); + } + else + { + tokenstr = HEXPREFIX+uint32ToHex(ui32val); + } + } // isShowIntsAsHex + } + + if (m_format_options.testFlag(ShowCtrlChars)) + { + tokenstr.replace(QChar(0x7f),QChar(0x2401)); + + for (int idx = 1; idx <= 0x1f; idx++) { + if (idx == '\n') continue; + tokenstr.replace(QChar(idx),QChar(idx+0x2400)); + // tokenstr.replace(QChar(idx), QString("<%1>").arg(uint8ToHex(idx))); + } + } // if ShowCtrlChars + + formattedText.append(tokenstr); + + } + + } // while tokenIt.hasNext() + + formattedText.append("\n"); + + } // foreach line + m_formattedText = formattedText; +} + +void ApplesoftFormatter::formatDocument(QTextDocument *doc) +{ + if (!doc) return; + + doc->clear(); + QTextCursor cursor(doc); + + foreach (ApplesoftLine line, m_file->getLines()) + { + QString linestring = QString("%1 ").arg(line.linenum,5,10,QChar(' ')); + cursor.insertText(linestring,ApplesoftToken::textFormat(ApplesoftToken::LineNumberTokenVal)); + + QVectorIteratortokenIt(line.tokens); + while (tokenIt.hasNext()) + { + ApplesoftToken token = tokenIt.next(); + bool isBranchTarget = false; + if (token.isOptFmtToken()) + { + switch (token.getTokenId()) + { + + case ApplesoftToken::OptFmtFlagFlowTargetNextTokenValue: + { + if (m_format_options.testFlag(ShowIntsAsHex)) + isBranchTarget = true; + break; + } + + case ApplesoftToken::OptFmtIndentLineBreakTokenValue: + { + if (m_format_options.testFlag(ReindentCode)) + cursor.insertBlock(); + break; + } + case ApplesoftToken::OptFmtIndentSpaceTokenValue: + { + if (m_format_options.testFlag(ReindentCode)) + cursor.insertText(" ",ApplesoftToken::defaultTextFormat()); + break; + } + case ApplesoftToken::OptFmtIndentTabTokenValue: + { + if (m_format_options.testFlag(ReindentCode)) + cursor.insertText(" ",ApplesoftToken::defaultTextFormat()); + break; + } + case ApplesoftToken::OptFmtLeadingSpaceTokenValue: + { + cursor.insertText(" ",ApplesoftToken::defaultTextFormat()); + break; + } + case ApplesoftToken::OptFmtReturnLineBreakTokenValue: + { + if (m_format_options.testFlag(BreakAfterReturn)) + cursor.insertBlock(); + break; + } + default: + { + break; + } + } + } // isOptFmt + else + { + QString tokenstr = token.getRawPrintableString(); + QTextCharFormat fmt = token.textFormat(); + + if (token.getTokenId() == ApplesoftToken::IntegerTokenVal) + { + if (m_format_options.testFlag(ShowIntsAsHex) && !isBranchTarget) + { + quint32 ui32val = token.getUnsignedIntegerValue(); + qint32 i32val = token.getIntegerValue(); + if ((i32val < 128 && i32val >= -128) || ui32val < 256) + { + quint8 ui8 = ui32val; + tokenstr = HEXPREFIX+uint8ToHex(ui8); + } + else if ((i32val < 32768 && i32val >= -32768) || ui32val < 65536) + { + quint16 ui16 = ui32val; + tokenstr = HEXPREFIX+uint16ToHex(ui16); + } + else + { + tokenstr = HEXPREFIX+uint32ToHex(ui32val); + } + } // isShowIntsAsHex + } + + if (m_format_options.testFlag(ShowCtrlChars)) + { + tokenstr.replace(QChar(0x7f),QChar(0x2401)); + + for (int idx = 1; idx <= 0x1f; idx++) { + if (idx == '\n') continue; + tokenstr.replace(QChar(idx),QChar(idx+0x2400)); + // tokenstr.replace(QChar(idx), QString("<%1>").arg(uint8ToHex(idx))); + } + } // if ShowCtrlChars + + //formattedText.append(tokenstr); + cursor.insertText(tokenstr,fmt); + + } + + } // while tokenIt.hasNext() + + //formattedText.append("\n"); + cursor.insertBlock(); + + } // foreach line +} diff --git a/src/applesoftfile/applesoftformatter.h b/src/applesoftfile/applesoftformatter.h index 580fc5f..6ca141a 100644 --- a/src/applesoftfile/applesoftformatter.h +++ b/src/applesoftfile/applesoftformatter.h @@ -35,11 +35,12 @@ public: FormatOptions flags() { return m_format_options; } void formatText(); - QSharedPointer getFormattedDocument() const { return m_formattedDocument; } QString getFormattedText() const { return m_formattedText; } QList > flowTargets() const { return m_flowTargets; } + void newFormatText(); + void formatDocument(QTextDocument *doc); signals: void newFile(ApplesoftFile *file); @@ -52,7 +53,6 @@ private: ApplesoftFile *m_file; QString m_formattedText; - QSharedPointer m_formattedDocument; }; diff --git a/src/applesoftfile/applesofttoken.cxx b/src/applesoftfile/applesofttoken.cxx index c1cfcbf..64c1b61 100644 --- a/src/applesoftfile/applesofttoken.cxx +++ b/src/applesoftfile/applesofttoken.cxx @@ -80,6 +80,9 @@ void ApplesoftToken::setTokenId(quint16 id) } else if (id == StringAryVarTokenVal) { m_token_type = STRING_ARY_VARIABLE_TOKEN; m_command_type = NONE; + } else if (id >= 0xe000 && id < 0xf000) { + m_token_type = OPTIONAL_FORMAT_TOKEN; + m_command_type = OPTIONAL_FORMAT; } } @@ -122,6 +125,8 @@ QString ApplesoftToken::getRawPrintableString() const return getStringValue(); } else if (m_token_id == StringAryVarTokenVal) { return getStringValue(); + } else if (m_token_id >= 0xe000 && m_token_id < 0xf000) { + return ""; } else { return "[temp undefined]"; } @@ -130,16 +135,55 @@ QString ApplesoftToken::getRawPrintableString() const QTextCharFormat ApplesoftToken::defaultTextFormat() { QTextCharFormat tf; // Default - tf.setFontFamily("Typewriter"); - tf.setFontPointSize(10); - tf.setForeground(Qt::red); +// tf.setFont(QFont("Courier")); +// tf.setFontPointSize(10); + tf.setForeground(Qt::black); return tf; } QTextCharFormat ApplesoftToken::textFormat(quint16 tokenType) { - makeTextCharFormats(); - return m_textcharformats[TCFDefault]; + QTextCharFormat tf = defaultTextFormat(); + + if (tokenType < 0x80) // Ascii + { + tf.setForeground(Qt::black); + } + else if (tokenType < 0x100) // Applesoft Tokens + { + tf.setForeground(Qt::black); + } + else if (tokenType == StringTokenVal) + { + tf.setForeground(Qt::blue); + tf.setFontWeight(QFont::Bold); + } + else if (tokenType == IntegerTokenVal || tokenType == FloatTokenVal) + { + tf.setForeground(Qt::darkGreen); + tf.setFontWeight(QFont::Bold); + } + else if (tokenType == StringVarTokenVal || + tokenType == StringAryVarTokenVal || + tokenType == IntVarTokenVal || + tokenType == IntAryVarTokenVal || + tokenType == FloatVarTokenVal || + tokenType == FloatAryVarTokenVal) + { + tf.setFontWeight(QFont::Bold); + tf.setForeground(Qt::darkMagenta); + } + else if (tokenType == RemStringTokenVal) + { + tf.setForeground(Qt::darkGray); + tf.setFontUnderline(true); + } + else if (tokenType == DataStringTokenVal) + { + tf.setForeground(Qt::darkRed); + } + + return tf; } @@ -159,9 +203,6 @@ void ApplesoftToken::makeTextCharFormats() // TCFRemString, // TCFUnknown - QTextCharFormat tf = defaultTextFormat(); - - m_textcharformats.insert(TCFDefault, tf); } diff --git a/src/applesoftfile/applesofttoken.h b/src/applesoftfile/applesofttoken.h index 9fef7f8..7201d15 100644 --- a/src/applesoftfile/applesofttoken.h +++ b/src/applesoftfile/applesofttoken.h @@ -6,6 +6,22 @@ #include #include +enum TextCharFormatType { + TCFDefault, + TCFCtrlChar, + TCFAscii, + TCFFunction, + TCFOperator, + TCFUnusedToken, + TCFNumber, + TCFString, + TCFVariable, + TCFDataString, + TCFRemString, + TCFUnknown +}; + + class ApplesoftToken { public: @@ -26,6 +42,15 @@ public: static const quint16 StringVarTokenVal = 0x109; static const quint16 StringAryVarTokenVal = 0x10A; + static const quint16 OptFmtLeadingSpaceTokenValue = 0xe000; + static const quint16 OptFmtIndentLineBreakTokenValue = 0xe001; + static const quint16 OptFmtIndentTabTokenValue = 0xe002; + static const quint16 OptFmtIndentSpaceTokenValue = 0xe003; + static const quint16 OptFmtFlagFlowTargetNextTokenValue = 0xe004; + static const quint16 OptFmtReturnLineBreakTokenValue = 0xe005; + + + static const quint16 LineNumberTokenVal = 0xfffe; static const quint16 DefaultTokenVal = 0xffff; @@ -117,7 +142,8 @@ public: FLOAT_VARIABLE_TOKEN = 0xB, FLOAT_ARY_VARIABLE_TOKEN = 0xC, STRING_VARIABLE_TOKEN = 0xD, - STRING_ARY_VARIABLE_TOKEN = 0xE + STRING_ARY_VARIABLE_TOKEN = 0xE, + OPTIONAL_FORMAT_TOKEN = 0xF } TokenType; typedef enum { @@ -125,7 +151,8 @@ public: COMMAND, OPERATOR, FUNCTION, - UNDEFINED_COMMAND + UNDEFINED_COMMAND, + OPTIONAL_FORMAT } CommandType; ApplesoftToken(); @@ -156,32 +183,20 @@ public: return textFormat(m_token_id); } - QTextCharFormat textFormat(quint16 tokentype) ; + static QTextCharFormat textFormat(quint16 tokentype) ; - static QString getStringForToken(quint8 token) { + QString getStringForToken(quint8 token) { if (m_tokens.size() == 0) { initializeTokenTable(); } return m_tokens[token]; } static QTextCharFormat defaultTextFormat(); + bool isOptFmtToken() const { return (m_token_id >= 0xe000 && m_token_id < 0xf000); } + private: void makeTextCharFormats(); - enum TextCharFormatType { - TCFDefault, - TCFCtrlChar, - TCFAscii, - TCFFunction, - TCFOperator, - TCFUnusedToken, - TCFNumber, - TCFString, - TCFVariable, - TCFDataString, - TCFRemString, - TCFUnknown - }; static QMap m_tokens; @@ -197,5 +212,4 @@ private: }; - #endif // APPLESOFTTOKEN_H diff --git a/src/ui/viewers/applesoftfileviewer.cxx b/src/ui/viewers/applesoftfileviewer.cxx index 40f5b77..a426d63 100644 --- a/src/ui/viewers/applesoftfileviewer.cxx +++ b/src/ui/viewers/applesoftfileviewer.cxx @@ -30,6 +30,7 @@ ApplesoftFileViewer::ApplesoftFileViewer(QWidget *parent) : setIndentCode(settings.value("ASViewer.indentCode",false).toBool(), NoReformat); setIntsAsHex(settings.value("ASViewer.intsAsHex",false).toBool(), NoReformat); + setBreakAfterReturn(settings.value("ASViewer.breakAfterReturn",false).toBool(), NoReformat); } ApplesoftFileViewer::~ApplesoftFileViewer() @@ -37,8 +38,6 @@ ApplesoftFileViewer::~ApplesoftFileViewer() delete ui; } - - bool ApplesoftFileViewer::makeMenuOptions(QMenu *menu) { QSettings settings; @@ -59,6 +58,14 @@ bool ApplesoftFileViewer::makeMenuOptions(QMenu *menu) connect(action, SIGNAL(toggled(bool)),SLOT(setIndentCode(bool))); menu->addAction(action); + action = new QAction("Blank &Line after RETURNs",menu); + action->setCheckable(true); + action->setChecked(settings.value("ASViewer.breakAfterReturn",false).toBool()); + setIndentCode(settings.value("ASViewer.breakAfterReturn",false).toBool(),NoReformat); + connect(action, SIGNAL(toggled(bool)), ui->findText,SLOT(clear())); + connect(action, SIGNAL(toggled(bool)),SLOT(setBreakAfterReturn(bool))); + menu->addAction(action); + menu->addSeparator(); action = new QAction("Show &Variable Explorer...",menu); @@ -113,6 +120,22 @@ void ApplesoftFileViewer::setIndentCode(bool enabled, ReformatRule reformat) reformatText(); } +void ApplesoftFileViewer::setBreakAfterReturn(bool enabled, ReformatRule reformat) +{ + if (enabled) + { + m_formatter->setFlags(m_formatter->flags() | ApplesoftFormatter::BreakAfterReturn); + } + else + { + m_formatter->setFlags(m_formatter->flags() & ~ApplesoftFormatter::BreakAfterReturn); + } + QSettings settings; + settings.setValue("ASViewer.breakAfterReturn",enabled); + if (reformat == ForceReformat) + reformatText(); +} + void ApplesoftFileViewer::setIntsAsHex(bool enabled, ReformatRule reformat) { if (enabled) @@ -131,9 +154,15 @@ void ApplesoftFileViewer::setIntsAsHex(bool enabled, ReformatRule reformat) void ApplesoftFileViewer::reformatText() { - m_formatter->formatText(); - ui->textArea->setText(m_formatter->getFormattedText()); - qDebug() << m_formatter->flowTargets(); + QTextDocument *doc = ui->textArea->document(); + + m_formatter->formatDocument(doc); + + QTextCursor cursor = ui->textArea->textCursor(); + cursor.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor); + ui->textArea->setTextCursor(cursor); + + //qDebug() << m_formatter->flowTargets(); } void ApplesoftFileViewer::setFile(GenericFile *file) { @@ -151,8 +180,11 @@ void ApplesoftFileViewer::setFile(ApplesoftFile *file) { QString title = QString("AppleSoft Viewer: %1").arg(m_file->filename()); m_title = title; setWindowTitle(title); - m_formatter->formatText(); - ui->textArea->setText(m_formatter->getFormattedText()); + QTextDocument *doc = ui->textArea->document(); + m_formatter->formatDocument(doc); + QTextCursor cursor = ui->textArea->textCursor(); + cursor.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor); + ui->textArea->setTextCursor(cursor); } void ApplesoftFileViewer::setData(QByteArray data) diff --git a/src/ui/viewers/applesoftfileviewer.h b/src/ui/viewers/applesoftfileviewer.h index 50e56c5..9b046a5 100644 --- a/src/ui/viewers/applesoftfileviewer.h +++ b/src/ui/viewers/applesoftfileviewer.h @@ -48,6 +48,7 @@ protected slots: void toggleWordWrap(bool enabled); void setIndentCode(bool enabled, ReformatRule reformat = ForceReformat); void setIntsAsHex(bool enabled, ReformatRule reformat = ForceReformat); + void setBreakAfterReturn(bool enabled, ReformatRule reformat = ForceReformat); void launchVarBrowser(); void reformatText();