diff options
Diffstat (limited to 'microbe/microbe.cpp')
-rw-r--r-- | microbe/microbe.cpp | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/microbe/microbe.cpp b/microbe/microbe.cpp new file mode 100644 index 0000000..d94cba7 --- /dev/null +++ b/microbe/microbe.cpp @@ -0,0 +1,472 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by Daniel Clarke [email protected] * + * Copyright (C) 2005 by David Saxton * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * 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., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "instruction.h" +#include "microbe.h" +#include "parser.h" +#include "optimizer.h" +#include "pic14.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qfile.h> + +#include <iostream> +using namespace std; + + +//BEGIN class Microbe +Microbe::Microbe() +{ + m_maxDelaySubroutine = PIC14::Delay_None; + m_dest = 0; + m_uniqueLabel = 0; + + // Hardwired constants + m_aliasList["true"] = "1"; + m_aliasList["false"] = "0"; + // Things starting with b are reserved by gpasm (for binary numbers) + m_aliasList["b"] = "_b"; + + //BEGIN Keypad values + int bv[4][6] = { + { 1, 2, 3, 10, 14, 18 }, + { 4, 5, 6, 11, 15, 19 }, + { 7, 8, 9, 12, 16, 20 }, + { 253, 0, 254, 13, 17, 21 } }; + + for ( unsigned row = 0; row < 4; ++row ) + { + for ( unsigned col = 0; col < 6; ++col ) + { + m_aliasList[ QString("Keypad_%1_%2").arg(row+1).arg(col+1) ] = QString::number( bv[row][col] ); + } + } + + m_aliasList[ "Keypad_None" ] = "0xff"; + //END Keypad values +} + + +Microbe::~Microbe() +{ +} + + +QString Microbe::compile( const QString & url, bool showSource, bool optimize ) +{ + QFile file( url ); + if( file.open( IO_ReadOnly ) ) + { + QTextStream stream(&file); + unsigned line = 0; + while( !stream.atEnd() ) + m_program += SourceLine( stream.readLine(), url, line++ ); + file.close(); + simplifyProgram(); + } + else + { + m_errorReport += i18n("Could not open file '%1'\n").arg(url); + return 0; + } + + Parser parser(this); + + // Extract the PIC ID + if ( !m_program.isEmpty() ) + { + m_picType = PIC14::toType( m_program[0].text() ); + m_program.remove( m_program.begin() ); + } + + PIC14 * pic = makePic(); + if ( !pic ) + return 0; + + Code * code = parser.parse( m_program ); + pic->setCode( code ); + pic->addCommonFunctions( (PIC14::DelaySubroutine)m_maxDelaySubroutine ); + + pic->postCompileConstruct( m_usedInterrupts ); + code->postCompileConstruct(); + + if ( optimize ) + { + Optimizer opt; + opt.optimize( code ); + } + + return code->generateCode( pic ); +} + + +PIC14 * Microbe::makePic() +{ + return new PIC14( this, (PIC14::Type)m_picType ); +} + + +void Microbe::simplifyProgram() +{ + SourceLineList simplified; + + enum CommentType { None, SingleLine, MultiLine }; + CommentType commentType = None; + + SourceLineList::const_iterator end = m_program.end(); + for ( SourceLineList::const_iterator it = m_program.begin(); it != end; ++it ) + { + QString code = (*it).text(); + QString simplifiedLine; + + if ( commentType == SingleLine ) + commentType = None; + + unsigned l = code.length(); + + for ( unsigned i = 0; i < l; ++i ) + { + QChar c = code[i]; + switch ( c ) + { + case '/': + // Look for start of comments in form "//" and "/*" + + if ( commentType == None && (i+1 < l) ) + { + if ( code[i+1] == '/' ) + { + commentType = SingleLine; + i++; + } + + else if ( code[i+1] == '*' ) + { + commentType = MultiLine; + i++; + } + } + break; + + case '*': + // Look for end of comments in form "*/" + if ( commentType == MultiLine && (i+1 < l) && code[i+1] == '/' ) + { + i++; + commentType = None; + continue; + } + break; + + case '{': + case '}': + // Put braces on seperate lines + + if ( commentType != None ) + break; + + simplified << SourceLine( simplifiedLine.simplifyWhiteSpace(), (*it).url(), (*it).line() ); + simplified << SourceLine( c, (*it).url(), (*it).line() ); + + simplifiedLine = ""; + continue; + } + + if ( commentType == None ) + simplifiedLine += c; + } + + simplified << SourceLine( simplifiedLine.simplifyWhiteSpace(), (*it).url(), (*it).line() ); + } + + m_program.clear(); + end = simplified.end(); + for ( SourceLineList::const_iterator it = simplified.begin(); it != end; ++it ) + { + if ( !(*it).text().isEmpty() ) + m_program << *it; + } +} + + +void Microbe::compileError( MistakeType type, const QString & context, const SourceLine & sourceLine ) +{ + QString message; + switch (type) + { + case UnknownStatement: + message = i18n("Unknown statement"); + break; + case InvalidPort: + message = i18n("Port '%1' is not supported by target PIC").arg(context); + break; + case UnassignedPin: + message = i18n("Pin identifier was not followed by '='"); + break; + case NonHighLowPinState: + message = i18n("Pin state can only be 'high' or 'low'"); + break; + case UnassignedPort: + message = i18n("Invalid token '%1'. Port identifier should be followed by '='").arg(context); + break; + case UnexpectedStatementBeforeBracket: + message = i18n("Unexpected statement before '{'"); + break; + case MismatchedBrackets: + message = i18n("Mismatched brackets in expression '%1'").arg(context); + break; + case InvalidEquals: + message = i18n("Invalid '=' found in expression"); + break; + case ReservedKeyword: + message = i18n("Reserved keyword '%1' cannot be a variable name.").arg(context); + break; + case ConsecutiveOperators: + message = i18n("Nothing between operators"); + break; + case MissingOperator: + message = i18n("Missing operator or space in operand"); + break; + case UnknownVariable: + if ( context.isEmpty() ) + message = i18n("Unknown variable"); + else + message = i18n("Unknown variable '%1'").arg(context); + break; + case UnopenableInclude: + message = i18n("Could not open include file '%1'").arg(context); + break; + case DivisionByZero: + message = i18n("Division by zero"); + break; + case NumberTooBig: + message = i18n("Number too big"); + break; + case NonConstantStep: + message = i18n("Step can only be a constant expression"); + break; + case NonConstantDelay: + message = i18n("Delay must be a positive constant value"); + break; + case HighLowExpected: + message = i18n("'high' or 'low' expected after pin expression '%1'").arg(context); + break; + case InvalidComparison: + message = i18n("Comparison operator in '%1' is not recognized"); + break; + case SubBeforeEnd: + message = i18n("Subroutine definition before end of program"); + break; + case InterruptBeforeEnd: + message = i18n("Interrupt routine definition before end of program"); + break; + case LabelExpected: + message = i18n("Label expected"); + break; + case TooManyTokens: + message = i18n("Extra tokens at end of line"); + break; + case FixedStringExpected: + message = i18n("Expected '%1'").arg(context); + break; + case PinListExpected: + message = i18n("Pin list expected"); + break; + case AliasRedefined: + message = i18n("Alias already definied"); + break; + case InvalidInterrupt: + message = i18n("Interrupt type not supported by target PIC"); + break; + case InterruptRedefined: + message = i18n("Interrupt already definied"); + break; + case ReadOnlyVariable: + message = i18n("Variable '%1' is read only").arg(context); + break; + case WriteOnlyVariable: + message = i18n("Variable '%1' is write only").arg(context); + break; + case InvalidPinMapSize: + message = i18n("Invalid pin list size"); + break; + case VariableRedefined: + message = i18n("Variable '%1' is already defined").arg(context); + break; + case InvalidVariableName: + message = i18n("'%1' is not a valid variable name").arg(context); + break; + case VariableExpected: + message = i18n("Variable expected"); + break; + case NameExpected: + message = i18n("Name expected"); + break; + } + + + m_errorReport += QString("%1:%2:Error [%3] %4\n") + .arg( sourceLine.url() ) + .arg( sourceLine.line()+1 ) + .arg( type ) + .arg( message ); +} + + +bool Microbe::isValidVariableName( const QString & variableName ) +{ + if ( variableName.isEmpty() ) + return false; + + if ( !variableName[0].isLetter() && variableName[0] != '_' ) + return false; + + for ( unsigned i = 1; i < variableName.length(); ++i ) + { + if ( !variableName[i].isLetterOrNumber() && variableName[i] != '_' ) + return false; + } + + return true; +} + + +void Microbe::addVariable( const Variable & variable ) +{ + if ( variable.type() == Variable::invalidType ) + return; + + if ( !isVariableKnown( variable.name() ) ) + m_variables << variable; +} + + +Variable Microbe::variable( const QString & name ) const +{ + VariableList::const_iterator end = m_variables.end(); + for ( VariableList::const_iterator it = m_variables.begin(); it != end; ++it ) + { + if ( (*it).name() == name ) + return *it; + } + return Variable(); +} + + +bool Microbe::isVariableKnown( const QString & name ) const +{ + return variable(name).type() != Variable::invalidType; +} + + +void Microbe::addDelayRoutineWanted( unsigned routine ) +{ + if ( m_maxDelaySubroutine < routine ) + m_maxDelaySubroutine = routine; +} + + +void Microbe::addAlias( const QString & name, const QString & dest ) +{ + m_aliasList[name] = dest; +} + + +QString Microbe::alias( const QString & alias ) const +{ + // If the string is an alias, return the real string, + // otherwise just return the alias as that is the real string. + AliasMap::const_iterator it = m_aliasList.find(alias); + if ( it != m_aliasList.constEnd() ) + return it.data(); + return alias; +} + + +void Microbe::setInterruptUsed(const QString &interruptName) +{ + // Don't add it again if it is already in the list + if ( m_usedInterrupts.contains( interruptName ) ) + return; + m_usedInterrupts.append(interruptName); +} + + +bool Microbe::isInterruptUsed( const QString & interruptName ) +{ + return m_usedInterrupts.contains( interruptName ); +} + + +QString Microbe::dest() const +{ + return QString("__op%1").arg(m_dest); +} + + +void Microbe::incDest() +{ + m_dest++; +// if ( ++m_dest > m_highestDest ) +// m_highestDest = m_dest; +} + + +void Microbe::decDest() +{ + m_dest--; +} + + +void Microbe::resetDest() +{ + m_dest = 0; +} +//END class Microbe + + + +//BEGIN class SourceLine +SourceLine::SourceLine( const QString & text, const QString & url, int line ) +{ + m_text = text; + m_url = url; + m_line = line; +} + + +SourceLine::SourceLine() +{ + m_line = -1; +} + + +QStringList SourceLine::toStringList( const SourceLineList & lines ) +{ + QStringList joined; + SourceLineList::const_iterator end = lines.end(); + for ( SourceLineList::const_iterator it = lines.begin(); it != end; ++it ) + joined << (*it).text(); + return joined; + +} +//END class SourceLine + |