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/src/languages/flowcode.cpp

497 lines
12 KiB

/***************************************************************************
* 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 <tqfile.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()
{
}
void FlowCode::processInput( ProcessOptions options )
{
m_processOptions = options;
if ( !options.p_flowCodeDocument )
{
options.p_flowCodeDocument = new FlowCodeDocument( TQString(), 0l );
options.p_flowCodeDocument->openURL( options.inputFiles().first() );
connect( this, TQT_SIGNAL(processSucceeded( Language *)), options.p_flowCodeDocument, TQT_SLOT(deleteLater()) );
connect( this, TQT_SIGNAL(processFailed( Language *)), options.p_flowCodeDocument, TQT_SLOT(deleteLater()) );
}
if ( !options.p_flowCodeDocument->microSettings() )
{
finish(false);
return;
}
TQFile 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 TQString code = generateMicrobe( options.p_flowCodeDocument->itemList(), options.p_flowCodeDocument->microSettings() );
if (code.isEmpty())
{
finish(false);
return;
}
TQTextStream stream(&file);
stream << code;
file.close();
finish(true);
}
void FlowCode::setStartPart( FlowPart *startPart )
{
p_startPart = startPart;
}
void FlowCode::addCode( const TQString& code )
{
m_code += code;
if ( !m_code.endsWith("\n") ) m_code += '\n';
}
bool FlowCode::isValidBranch( FlowPart *flowPart )
{
return flowPart && (flowPart->level() >= m_curLevel) && !m_stopParts.contains(flowPart);
}
void FlowCode::addCodeBranch( FlowPart * flowPart )
{
if (!flowPart)
return;
if ( !isValidBranch(flowPart) )
return;
if ( m_addedParts.contains(flowPart) )
{
const TQString 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 TQString labelName = genLabel(flowPart->id());
addCode(labelName+':');
m_labels.append(labelName);
flowPart->generateMicrobe(this);
m_curLevel = prevLevel;
}
}
TQString FlowCode::genLabel( const TQString &id )
{
return "__label_"+id;
}
void FlowCode::addStopPart( FlowPart *part )
{
if (part) m_stopParts.append(part);
}
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);
}
TQString 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 = TQString();
// PIC type
{
const TQString codeString = settings->microInfo()->id() + "\n";
addCode(codeString);
}
// Initial variables
{
TQStringList vars = settings->variableNames();
// If "inited" is true at the end, we comment at the insertion point
bool inited = false;
const TQString codeString = "// Initial variable values:\n";
addCode(codeString);
const TQStringList::iterator end = vars.end();
for ( TQStringList::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 )
{
TQString 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( TQString("%1 %2 %3").arg( type ).arg( it.key() ).arg( it.data().pins().join(" ") ) );
}
}
// Initial port settings
{
TQStringList portNames = settings->microInfo()->package()->portNames();
const TQStringList::iterator end = portNames.end();
// TRIS registers (remember that this is set to ..11111 on all resets)
for ( TQStringList::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 )
{
TQString name = *it;
name.replace("PORT","TRIS");
addCode( name+" = "+TQString::number(portType) );
}
}
// PORT registers
for ( TQStringList::iterator it = portNames.begin(); it != end; ++it )
{
const int portState = settings->portState(*it);
addCode( (*it)+" = "+TQString::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;
}
void FlowCode::tidyCode()
{
// First, get rid of the unused labels
const TQStringList::iterator end = m_labels.end();
for ( TQStringList::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");
TQString 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;
}
void FlowCode::addSubroutine( FlowPart *part )
{
if ( !part || m_subroutines.contains(part) || part->parentItem() || !dynamic_cast<FlowContainer*>(part) ) return;
m_subroutines.append(part);
}
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;
}