/*************************************************************************** * Copyright (C) 2004-2005 by Daniel Clarke daniel.jc@gmail.com * * 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 #include #include #include 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[ TQString("Keypad_%1_%2").arg(row+1).arg(col+1) ] = TQString::number( bv[row][col] ); } } m_aliasList[ "Keypad_None" ] = "0xff"; //END Keypad values } Microbe::~Microbe() { } TQString Microbe::compile( const TQString & url, bool showSource, bool optimize ) { TQFile file( url ); if( file.open( IO_ReadOnly ) ) { TQTextStream 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 ) { TQString code = (*it).text(); TQString simplifiedLine; if ( commentType == SingleLine ) commentType = None; unsigned l = code.length(); for ( unsigned i = 0; i < l; ++i ) { TQChar 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 TQString & context, const SourceLine & sourceLine ) { TQString 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 += TQString("%1:%2:Error [%3] %4\n") .arg( sourceLine.url() ) .arg( sourceLine.line()+1 ) .arg( type ) .arg( message ); } bool Microbe::isValidVariableName( const TQString & 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 TQString & 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 TQString & name ) const { return variable(name).type() != Variable::invalidType; } void Microbe::addDelayRoutineWanted( unsigned routine ) { if ( m_maxDelaySubroutine < routine ) m_maxDelaySubroutine = routine; } void Microbe::addAlias( const TQString & name, const TQString & dest ) { m_aliasList[name] = dest; } TQString Microbe::alias( const TQString & 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 TQString &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 TQString & interruptName ) { return m_usedInterrupts.contains( interruptName ); } TQString Microbe::dest() const { return TQString("__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 TQString & text, const TQString & url, int line ) { m_text = text; m_url = url; m_line = line; } SourceLine::SourceLine() { m_line = -1; } TQStringList SourceLine::toStringList( const SourceLineList & lines ) { TQStringList 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