Logo Search packages:      
Sourcecode: ktechlab version File versions

flowcode.cpp

/***************************************************************************
 *   Copyright (C) 2003-2005 by David Saxton                               *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

#include "flowcodedocument.h"
#include "flowcode.h"
#include "flowcontainer.h"
#include "flowpart.h"
#include "microsettings.h"
#include "microinfo.h"
#include "micropackage.h"
#include "node.h"
#include "pinmapping.h"

#include <klocale.h>
// #include <kmessagebox.h>
#include <qfile.h>

FlowCode::FlowCode( ProcessChain *processChain, KTechlab *parent )
      : Language( processChain, parent, i18n("FlowCode") )
{
      m_successfulMessage = i18n("*** Microbe generation successful ***");
      m_failedMessage = i18n("*** Microbe generation failed ***");
      p_startPart = 0l;
}

FlowCode::~FlowCode()
{
}


00038 void FlowCode::processInput( ProcessOptions options )
{
      m_processOptions = options;
      
      if ( !options.p_flowCodeDocument )
      {
            options.p_flowCodeDocument = new FlowCodeDocument( QString::null, 0l );
            options.p_flowCodeDocument->openURL( options.inputFiles().first() );
            
            connect( this, SIGNAL(processSucceeded( Language *)), options.p_flowCodeDocument, SLOT(deleteLater()) );
            connect( this, SIGNAL(processFailed( Language *)), options.p_flowCodeDocument, SLOT(deleteLater()) );
      }

      if ( !options.p_flowCodeDocument->microSettings() )
      {
            finish(false);
            return;
      }

      QFile file(options.intermediaryOutput());
      if ( file.open(IO_WriteOnly | IO_ReadOnly) == false )
      {
            finish(false);
            return;
      }
      file.close();
      
      if ( file.open(IO_WriteOnly) == false )
      {
            finish(false);
            return;
      }

      const QString code = generateMicrobe( options.p_flowCodeDocument->itemList(), options.p_flowCodeDocument->microSettings() );
      if (code.isEmpty())
      {
            finish(false);
            return;
      }

      QTextStream stream(&file);
      stream << code;
      file.close();
      finish(true);
}


00085 void FlowCode::setStartPart( FlowPart *startPart )
{
      p_startPart = startPart;
}


00091 void FlowCode::addCode( const QString& code )
{
      m_code += code;
      if ( !m_code.endsWith("\n") ) m_code += '\n';
}

00097 bool FlowCode::isValidBranch( FlowPart *flowPart )
{
      return flowPart && (flowPart->level() >= m_curLevel) && !m_stopParts.contains(flowPart);
}

00102 void FlowCode::addCodeBranch( FlowPart * flowPart )
{
      if (!flowPart)
            return;
      
      if ( !isValidBranch(flowPart) )
            return;
      
      if ( m_addedParts.contains(flowPart) )
      {
            const QString labelName = genLabel(flowPart->id());
            addCode( "goto "+labelName );
            m_gotos.append(labelName);
            return;
      }
      else
      {
            m_addedParts.append(flowPart);
            int prevLevel = m_curLevel;
            m_curLevel = flowPart->level();
            
            const QString labelName = genLabel(flowPart->id());
            addCode(labelName+':');
            m_labels.append(labelName);
            
            flowPart->generateMicrobe(this);
            m_curLevel = prevLevel;
      }
}

00132 QString FlowCode::genLabel( const QString &id )
{
      return "__label_"+id;
}

00137 void FlowCode::addStopPart( FlowPart *part )
{
      if (part) m_stopParts.append(part);
}

00142 void FlowCode::removeStopPart( FlowPart *part )
{
      if (!part) return;
      
      // We only want to remove one instance of the FlowPart, in case it has been
      // used as a StopPart for more than one FlowPart
      FlowPartList::iterator it = m_stopParts.find(part);
      if ( it != m_stopParts.end() ) m_stopParts.remove(it);
}

00152 QString FlowCode::generateMicrobe( const ItemList &itemList, MicroSettings *settings )
{
      bool foundStart = false;
      const ItemList::const_iterator end = itemList.end();
      for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it )
      {
            if (!*it)
                  continue;
            
            FlowPart * startPart = dynamic_cast<FlowPart*>((Item*)*it);
            
            if (!startPart)
                  continue;
            
            // Check to see if we have any floating connections
            const NodeMap nodeMap = startPart->nodeMap();
            NodeMap::const_iterator nodeMapEnd = nodeMap.end();
            for ( NodeMap::const_iterator nodeMapIt = nodeMap.begin(); nodeMapIt != nodeMapEnd; ++nodeMapIt )
            {
                  Node * node = nodeMapIt.data().node;
                  if ( !node || (node->type() != Node::fp_out) )
                        continue;
                  
                  if ( !startPart->outputPart( nodeMapIt.key() ) )
                        outputWarning( i18n("Warning: Floating connection for %1").arg( startPart->id() ) );
            }
            
            FlowContainer * fc = dynamic_cast<FlowContainer*>((Item*)*it);
            
            if ( (*it)->id().startsWith("START") && startPart )
            {
                  foundStart = true;
                  setStartPart(startPart);
            }
            else if ( ((*it)->id().startsWith("interrupt") || (*it)->id().startsWith("sub")) && fc )
            {
                  addSubroutine(fc);
            }
      }
      
      if (!foundStart)
      {
            outputError( i18n("KTechlab was unable to find the \"Start\" part.\nThis must be included as the starting point for your program.") );
            return 0;
      }
      
      m_addedParts.clear();
      m_stopParts.clear();
      m_gotos.clear();
      m_labels.clear();
      m_code = QString::null;
      
      // PIC type
      {
            const QString codeString = settings->microInfo()->id() + "\n";
            addCode(codeString);
      }
      
      // Initial variables
      {
            QStringList vars = settings->variableNames();
            
            // If "inited" is true at the end, we comment at the insertion point
            bool inited = false;
            const QString codeString = "// Initial variable values:\n";
            addCode(codeString);
            
            const QStringList::iterator end = vars.end();
            for ( QStringList::iterator it = vars.begin(); it != end; ++it )
            {
                  VariableInfo *info = settings->variableInfo(*it);
                  if ( info /*&& info->initAtStart*/ )
                  {
                        inited = true;
                        addCode(*it+" = "+info->valueAsString());
                  }
            }
            if (!inited) {
                  m_code.remove(codeString);
            } else {
                  addCode("\n");
            }
      }
      
      // Initial pin maps
      {
            const PinMappingMap pinMappings = settings->pinMappings();
            PinMappingMap::const_iterator end = pinMappings.end();
            for ( PinMappingMap::const_iterator it = pinMappings.begin(); it != end; ++it )
            {
                  QString type;
                  
                  switch ( it.data().type() )
                  {
                        case PinMapping::Keypad_4x3:
                        case PinMapping::Keypad_4x4:
                              type = "keypad";
                              break;
                              
                        case PinMapping::SevenSegment:
                              type = "sevenseg";
                              break;
                              
                        case PinMapping::Invalid:
                              break;
                  }
                  
                  if ( type.isEmpty() )
                        continue;
                  
                  addCode( QString("%1 %2 %3").arg( type ).arg( it.key() ).arg( it.data().pins().join(" ") ) );
            }
      }
      
      // Initial port settings
      {
            QStringList portNames = settings->microInfo()->package()->portNames();
            const QStringList::iterator end = portNames.end();
            
            // TRIS registers (remember that this is set to ..11111 on all resets)
            for ( QStringList::iterator it = portNames.begin(); it != end; ++it )
            {
                  const int portType = settings->portType(*it);
                  const int pinCount = settings->microInfo()->package()->pinCount( 0, *it );
                  
                  // We don't need to reset it if portType == 2^(pinCount-1)
                  if ( portType != (1<<pinCount)-1 )
                  {
                        QString name = *it;
                        name.replace("PORT","TRIS");
                        addCode( name+" = "+QString::number(portType) );
                  }
            }
            
            // PORT registers
            for ( QStringList::iterator it = portNames.begin(); it != end; ++it )
            {
                  const int portState = settings->portState(*it);
                  addCode( (*it)+" = "+QString::number(portState) );
            }
      }
      
      
      m_curLevel = p_startPart->level();
      addCodeBranch(p_startPart);
      addCode("end");
      
      {
            const FlowPartList::iterator end = m_subroutines.end();
            for ( FlowPartList::iterator it = m_subroutines.begin(); it != end; ++it )
            {
                  m_curLevel = 0;
                  if (*it)
                  {
                        addCode("\n");
                        addCodeBranch(*it);
                  }
            }
      }
      
      tidyCode();
      return m_code;
}

00316 void FlowCode::tidyCode()
{
      // First, get rid of the unused labels
      const QStringList::iterator end = m_labels.end();
      for ( QStringList::iterator it = m_labels.begin(); it != end; ++it )
      {
            if ( !m_gotos.contains(*it) ) m_code.remove(*it+':');
      }
      
      
      // And now on to handling indentation :-)

      if ( !m_code.endsWith("\n") ) m_code.append("\n");
      QString newCode;
      bool multiLineComment = false; // For "/*"..."*/"
      bool comment = false; // For "//"
      bool asmEmbed = false;
      bool asmEmbedAllowed = true;
      bool asmKeyword = false;
      int asmEmbedLevel = -1;
      int level = 0;
      
      int pos=-1;
      const int length = m_code.length();
      while ( ++pos<length )
      {
            switch ( m_code[pos].latin1() )
            {
                  case '\n':
                  {
                        if (comment && !multiLineComment) comment = false;
                        newCode += '\n';
                        if ( !comment && !asmEmbed )
                        {
                              while ( pos+1<length && m_code[pos+1].isSpace() ) pos++;
                              bool closeBrace = false;
                              if ( pos+1<length && m_code[pos+1] == '}' )
                              {
                                    level--;
                                    pos++;
                                    closeBrace = true;
                              }
                              for ( int i=0; i<level; i++ ) newCode += '\t';
                              if (closeBrace) newCode += '}';
                              asmEmbedAllowed = true;
                        }
                        break;
                  } 
                  case '/':
                  {
                        newCode += '/';
                        if ( pos+1<length )
                        {
                              if          ( m_code[pos+1] == '/' ) comment = true;
                              else if ( m_code[pos+1] == '*' ) multiLineComment = comment = true;
                              newCode += m_code[++pos];
                        }
                        asmEmbedAllowed = false;
                        asmKeyword = false;
                        break;
                  }
                  case '*':
                  {
                        newCode += '*';
                        if ( pos+1<length )
                        {
                              if ( m_code[pos++] == '/' && multiLineComment ) comment = multiLineComment = false;
                              newCode += m_code[pos];
                        }
                        asmEmbedAllowed = false;
                        asmKeyword = false;
                        break;
                  }
                  case '{':
                  {
                        if (asmKeyword) {
                              asmEmbed = true;
                              asmEmbedLevel = level;
                        }
                        
                        if ( !comment ) level++;
                        newCode += '{';
                        
                        asmEmbedAllowed = false;
                        asmKeyword = false;
                        break;
                  }
                  case '}':
                  {
                        if ( !comment ) level--;
                        
                        if (asmEmbed && asmEmbedLevel == level) 
                        {
                              asmEmbed = false;
                              newCode += "\n";
                              for ( int i=0; i<level; i++ ) newCode += '\t';
                        }
                        newCode += '}';
                        
                        asmEmbedAllowed = true;
                        asmKeyword = false;
                        break;
                  }
                  case 'a':
                  {
                        newCode += m_code[pos];
                        if ( asmEmbedAllowed && !comment && pos+2<length )
                        {
                              if ( m_code[pos+1] == 's' && m_code[pos+2] == 'm' )
                              {
                                    asmKeyword = true;
                                    newCode += "sm";
                                    pos += 2;
                              }
                        }
                        break;
                  }
                  default:
                  {
                        asmEmbedAllowed = false;
                        asmKeyword = false;
                        newCode += m_code[pos];
                        break;
                  }
            }
      }
      m_code = newCode;
}

00445 void FlowCode::addSubroutine( FlowPart *part )
{
      if ( !part || m_subroutines.contains(part) || part->parentItem() || !dynamic_cast<FlowContainer*>(part) ) return;
      m_subroutines.append(part);
}


00452 ProcessOptions::ProcessPath::Path FlowCode::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const
{
      switch (inputPath)
      {
            case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute:
                  return ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute;
                  
            case ProcessOptions::ProcessPath::FlowCode_Microbe:
                  return ProcessOptions::ProcessPath::None;
                  
            case ProcessOptions::ProcessPath::FlowCode_PIC:
                  return ProcessOptions::ProcessPath::Microbe_PIC;
                  
            case ProcessOptions::ProcessPath::FlowCode_Program:
                  return ProcessOptions::ProcessPath::Microbe_Program;
                  
            case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC:
            case ProcessOptions::ProcessPath::AssemblyAbsolute_Program:
            case ProcessOptions::ProcessPath::AssemblyRelocatable_Library:
            case ProcessOptions::ProcessPath::AssemblyRelocatable_Object:
            case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC:
            case ProcessOptions::ProcessPath::AssemblyRelocatable_Program:
            case ProcessOptions::ProcessPath::C_AssemblyRelocatable:
            case ProcessOptions::ProcessPath::C_Library:
            case ProcessOptions::ProcessPath::C_Object:
            case ProcessOptions::ProcessPath::C_PIC:
            case ProcessOptions::ProcessPath::C_Program:
            case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute:
            case ProcessOptions::ProcessPath::Microbe_PIC:
            case ProcessOptions::ProcessPath::Microbe_Program:
            case ProcessOptions::ProcessPath::Object_Disassembly:
            case ProcessOptions::ProcessPath::Object_Library:
            case ProcessOptions::ProcessPath::Object_PIC:
            case ProcessOptions::ProcessPath::Object_Program:
            case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute:
            case ProcessOptions::ProcessPath::Program_Disassembly:
            case ProcessOptions::ProcessPath::Program_PIC:
            case ProcessOptions::ProcessPath::Invalid:
            case ProcessOptions::ProcessPath::None:
                  return ProcessOptions::ProcessPath::Invalid;
      }
      
      return ProcessOptions::ProcessPath::Invalid;
}


Generated by  Doxygen 1.6.0   Back to index