You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktechlab/microbe/microbe.cpp

473 lines
11 KiB

/***************************************************************************
* 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 <kdebug.h>
#include <klocale.h>
#include <tqfile.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[ TQString("Keypad_%1_%2").tqarg(row+1).tqarg(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").tqarg(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").tqarg(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 '='").tqarg(context);
break;
case UnexpectedStatementBeforeBracket:
message = i18n("Unexpected statement before '{'");
break;
case MismatchedBrackets:
message = i18n("Mismatched brackets in expression '%1'").tqarg(context);
break;
case InvalidEquals:
message = i18n("Invalid '=' found in expression");
break;
case ReservedKeyword:
message = i18n("Reserved keyword '%1' cannot be a variable name.").tqarg(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'").tqarg(context);
break;
case UnopenableInclude:
message = i18n("Could not open include file '%1'").tqarg(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'").tqarg(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'").tqarg(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").tqarg(context);
break;
case WriteOnlyVariable:
message = i18n("Variable '%1' is write only").tqarg(context);
break;
case InvalidPinMapSize:
message = i18n("Invalid pin list size");
break;
case VariableRedefined:
message = i18n("Variable '%1' is already defined").tqarg(context);
break;
case InvalidVariableName:
message = i18n("'%1' is not a valid variable name").tqarg(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")
.tqarg( sourceLine.url() )
.tqarg( sourceLine.line()+1 )
.tqarg( type )
.tqarg( 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").tqarg(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