|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2004-2005 by Daniel Clarke *
|
|
|
|
* daniel.jc@gmail.com *
|
|
|
|
* *
|
|
|
|
* 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 "btreebase.h"
|
|
|
|
#include "expression.h"
|
|
|
|
#include "instruction.h"
|
|
|
|
#include "parser.h"
|
|
|
|
#include "pic14.h"
|
|
|
|
#include "traverser.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqstring.h>
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN class Parser
|
|
|
|
Parser::Parser( Microbe * _mb )
|
|
|
|
{
|
|
|
|
m_code = 0;
|
|
|
|
m_pPic = 0;
|
|
|
|
mb = _mb;
|
|
|
|
// Set up statement definitions.
|
|
|
|
StatementDefinition definition;
|
|
|
|
|
|
|
|
definition.append( Field(Field::Label, "label") );
|
|
|
|
m_definitionMap["goto"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Label, "label") );
|
|
|
|
m_definitionMap["call"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Expression, "expression") );
|
|
|
|
definition.append( Field(Field::Code, "code") );
|
|
|
|
m_definitionMap["while"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
m_definitionMap["end"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Label, "label") );
|
|
|
|
definition.append( Field(Field::Code, "code") );
|
|
|
|
// For backwards compataibility
|
|
|
|
m_definitionMap["sub"] = definition;
|
|
|
|
m_definitionMap["subroutine"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Label, "label") );
|
|
|
|
definition.append( Field(Field::Code, "code") );
|
|
|
|
m_definitionMap["interrupt"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Label, "alias") );
|
|
|
|
definition.append( Field(Field::Label, "dest") );
|
|
|
|
m_definitionMap["alias"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Expression, "expression") );
|
|
|
|
definition.append( Field(Field::FixedString, 0, "then", true) );
|
|
|
|
definition.append( Field(Field::Code, "ifCode") );
|
|
|
|
definition.append( Field(Field::Newline) );
|
|
|
|
definition.append( Field(Field::FixedString, 0, "else", false) );
|
|
|
|
definition.append( Field(Field::Code, "elseCode") );
|
|
|
|
m_definitionMap["if"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Expression, "initExpression") );
|
|
|
|
definition.append( Field(Field::FixedString, 0, "to", true) );
|
|
|
|
definition.append( Field(Field::Expression, "toExpression") );
|
|
|
|
definition.append( Field(Field::FixedString, 0, "step", false) );
|
|
|
|
definition.append( Field(Field::Expression, "stepExpression") );
|
|
|
|
definition.append( Field(Field::Code, "code") );
|
|
|
|
m_definitionMap["for"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Variable, "variable") );
|
|
|
|
m_definitionMap["decrement"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Variable, "variable") );
|
|
|
|
m_definitionMap["increment"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Variable, "variable") );
|
|
|
|
m_definitionMap["rotateleft"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Variable, "variable") );
|
|
|
|
m_definitionMap["rotateright"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Code, "code") );
|
|
|
|
m_definitionMap["asm"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Expression, "expression") );
|
|
|
|
m_definitionMap["delay"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Code, "code") );
|
|
|
|
definition.append( Field(Field::Newline) );
|
|
|
|
definition.append( Field(Field::FixedString, 0, "until", true) );
|
|
|
|
definition.append( Field(Field::Expression, "expression") );
|
|
|
|
m_definitionMap["repeat"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Name, "name") );
|
|
|
|
definition.append( Field(Field::PinList, "pinlist") );
|
|
|
|
m_definitionMap["sevenseg"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
|
|
|
|
definition.append( Field(Field::Name, "name") );
|
|
|
|
definition.append( Field(Field::PinList, "pinlist") );
|
|
|
|
m_definitionMap["keypad"] = definition;
|
|
|
|
definition.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
Parser::~Parser()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Parser* Parser::createChildParser()
|
|
|
|
{
|
|
|
|
Parser * parser = new Parser( mb );
|
|
|
|
|
|
|
|
return parser;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Code * Parser::parseWithChild( const SourceLineList & lines )
|
|
|
|
{
|
|
|
|
Parser * p = createChildParser();
|
|
|
|
Code * code = p->parse(lines);
|
|
|
|
delete p;
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Code * Parser::parse( const SourceLineList & lines )
|
|
|
|
{
|
|
|
|
StatementList sList;
|
|
|
|
m_pPic = mb->makePic();
|
|
|
|
m_code = new Code();
|
|
|
|
m_pPic->setCode( m_code );
|
|
|
|
m_pPic->setParser(this);
|
|
|
|
m_bPassedEnd = false;
|
|
|
|
|
|
|
|
/* First pass
|
|
|
|
==========
|
|
|
|
|
|
|
|
Here we go through the code making each line into a statement object,
|
|
|
|
looking out for braced code as we go, if we find it then we put then
|
|
|
|
we make attach the braced code to the statment.
|
|
|
|
*/
|
|
|
|
|
|
|
|
SourceLineList::const_iterator end = lines.end();
|
|
|
|
for ( SourceLineList::const_iterator slit = lines.begin(); slit != end; ++slit )
|
|
|
|
{
|
|
|
|
Statement s;
|
|
|
|
s.content = *slit;
|
|
|
|
|
|
|
|
// Check to see if the line after next is a brace
|
|
|
|
SourceLineList::const_iterator previous = slit;
|
|
|
|
if ( (++slit != end) && (*slit).text() == "{" )
|
|
|
|
s.bracedCode = getBracedCode( & slit, end );
|
|
|
|
else
|
|
|
|
slit = previous;
|
|
|
|
|
|
|
|
if ( !s.text().isEmpty() )
|
|
|
|
sList.append(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
mb->resetDest();
|
|
|
|
|
|
|
|
for( StatementList::Iterator sit = sList.begin(); sit != sList.end(); ++sit )
|
|
|
|
{
|
|
|
|
m_currentSourceLine = (*sit).content;
|
|
|
|
|
|
|
|
TQString line = (*sit).text();
|
|
|
|
|
|
|
|
TQString command; // e.g. "delay", "for", "subroutine", "increment", etc
|
|
|
|
{
|
|
|
|
int spacepos = line.find(' ');
|
|
|
|
if ( spacepos >= 0 )
|
|
|
|
command = line.left( spacepos );
|
|
|
|
else
|
|
|
|
command = line;
|
|
|
|
}
|
|
|
|
OutputFieldMap fieldMap;
|
|
|
|
|
|
|
|
if ( (*sit).content.line() >= 0 )
|
|
|
|
m_code->append( new Instr_sourceCode( TQString("#MSRC\t%1; %2\t%3").arg( (*sit).content.line() + 1 ).arg( (*sit).content.url() ).arg( (*sit).content.text() ) ));
|
|
|
|
bool showBracesInSource = (*sit).hasBracedCode();
|
|
|
|
if ( showBracesInSource )
|
|
|
|
m_code->append(new Instr_sourceCode("{"));
|
|
|
|
|
|
|
|
// Use the first token in the line to look up the statement type
|
|
|
|
DefinitionMap::Iterator dmit = m_definitionMap.find(command);
|
|
|
|
if(dmit == m_definitionMap.end())
|
|
|
|
{
|
|
|
|
if( !processAssignment( (*sit).text() ) )
|
|
|
|
{
|
|
|
|
// Not an assignement, maybe a label
|
|
|
|
if( (*sit).isLabel() )
|
|
|
|
{
|
|
|
|
TQString label = (*sit).text().left( (*sit).text().length() - 1 );
|
|
|
|
///TODO sanity check label name and then do error like "Bad label"
|
|
|
|
m_pPic->Slabel( label );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
mistake( Microbe::Microbe::UnknownStatement );
|
|
|
|
}
|
|
|
|
|
|
|
|
continue; // Give up on the current statement
|
|
|
|
}
|
|
|
|
StatementDefinition definition = dmit.data();
|
|
|
|
|
|
|
|
// Start at the first white space character following the statement name
|
|
|
|
int newPosition = 0;
|
|
|
|
int position = command.length() + 1;
|
|
|
|
|
|
|
|
// Temporaries for use inside the switch
|
|
|
|
Field nextField;
|
|
|
|
Statement nextStatement;
|
|
|
|
|
|
|
|
bool errorInLine = false;
|
|
|
|
bool finishLine = false;
|
|
|
|
|
|
|
|
for( StatementDefinition::Iterator sdit = definition.begin(); sdit != definition.end(); ++sdit )
|
|
|
|
{
|
|
|
|
// If there is an error, or we have finished the statement,
|
|
|
|
// the stop. If we are at the end of a line in a multiline, then
|
|
|
|
// break to fall through to the next line
|
|
|
|
if( errorInLine || finishLine) break;
|
|
|
|
|
|
|
|
Field field = (*sdit);
|
|
|
|
TQString token;
|
|
|
|
|
|
|
|
bool saveToken = false;
|
|
|
|
bool saveBraced = false;
|
|
|
|
bool saveSingleLine = false;
|
|
|
|
|
|
|
|
switch(field.type())
|
|
|
|
{
|
|
|
|
case (Field::Label):
|
|
|
|
case (Field::Variable):
|
|
|
|
case (Field::Name):
|
|
|
|
{
|
|
|
|
newPosition = line.find( ' ', position );
|
|
|
|
if(position == newPosition)
|
|
|
|
{
|
|
|
|
newPosition = -1;
|
|
|
|
token = line.mid(position);
|
|
|
|
}
|
|
|
|
else token = line.mid(position, newPosition - position);
|
|
|
|
if( token.isEmpty() )
|
|
|
|
{
|
|
|
|
if(field.type() == Field::Label)
|
|
|
|
mistake( Microbe::Microbe::LabelExpected );
|
|
|
|
else if (field.type() == Field::Variable)
|
|
|
|
mistake( Microbe::VariableExpected );
|
|
|
|
else // field.type() == Field::Name
|
|
|
|
mistake( Microbe::NameExpected );
|
|
|
|
errorInLine = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
position = newPosition;
|
|
|
|
saveToken = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case (Field::Expression):
|
|
|
|
{
|
|
|
|
// This is slightly different, as there is nothing
|
|
|
|
// in particular that delimits an expression, we just have to
|
|
|
|
// look at what comes next and hope we can use that.
|
|
|
|
StatementDefinition::Iterator it(sdit);
|
|
|
|
++it;
|
|
|
|
if( it != definition.end() )
|
|
|
|
{
|
|
|
|
nextField = (*it);
|
|
|
|
if(nextField.type() == Field::FixedString)
|
|
|
|
newPosition = line.find(TQRegExp("\\b" + nextField.string() + "\\b"));
|
|
|
|
// Although code is not neccessarily braced, after an expression it is the only
|
|
|
|
// sensilbe way to have it.
|
|
|
|
else if(nextField.type() == Field::Code)
|
|
|
|
{
|
|
|
|
newPosition = line.find("{");
|
|
|
|
if(newPosition == -1) newPosition = line.length() + 1;
|
|
|
|
}
|
|
|
|
else if(nextField.type() == Field::Newline)
|
|
|
|
newPosition = line.length()+1;
|
|
|
|
else kdDebug() << "Bad statement definition - awkward field type after expression";
|
|
|
|
}
|
|
|
|
else newPosition = line.length() + 1;
|
|
|
|
if(newPosition == -1)
|
|
|
|
{
|
|
|
|
// Something was missing, we'll just play along for now,
|
|
|
|
// the next iteration will catch whatever was supposed to be there
|
|
|
|
}
|
|
|
|
token = line.mid(position, newPosition - position);
|
|
|
|
position = newPosition;
|
|
|
|
saveToken = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Field::PinList):
|
|
|
|
{
|
|
|
|
// For now, just assume that the list of pins will continue to the end of the tokens.
|
|
|
|
// (we could check until we come across a non-pin, but no command has that format at
|
|
|
|
// the moment).
|
|
|
|
|
|
|
|
token = line.mid( position + 1 );
|
|
|
|
position = line.length() + 1;
|
|
|
|
if ( token.isEmpty() )
|
|
|
|
mistake( Microbe::PinListExpected );
|
|
|
|
else
|
|
|
|
saveToken = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case (Field::Code):
|
|
|
|
if ( !(*sit).hasBracedCode() )
|
|
|
|
{
|
|
|
|
saveSingleLine = true;
|
|
|
|
token = line.mid(position);
|
|
|
|
position = line.length() + 1;
|
|
|
|
}
|
|
|
|
else if( position != -1 && position <= int(line.length()) )
|
|
|
|
{
|
|
|
|
mistake( Microbe::UnexpectedStatementBeforeBracket );
|
|
|
|
errorInLine = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Because of the way the superstructure parsing works there is no
|
|
|
|
// 'next line' as it were, the braced code is attached to the current line.
|
|
|
|
saveBraced = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Field::FixedString):
|
|
|
|
{
|
|
|
|
// Is the string found, and is it starting in the right place?
|
|
|
|
int stringPosition = line.find(TQRegExp("\\b"+field.string()+"\\b"));
|
|
|
|
if( stringPosition != position || stringPosition == -1 )
|
|
|
|
{
|
|
|
|
if( !field.compulsory() )
|
|
|
|
{
|
|
|
|
position = -1;
|
|
|
|
// Skip the next field
|
|
|
|
++sdit;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Otherwise raise an appropriate error
|
|
|
|
mistake( Microbe::FixedStringExpected, field.string() );
|
|
|
|
errorInLine = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
position += field.string().length() + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Field::Newline):
|
|
|
|
// It looks like the best way to handle this is to just actually
|
|
|
|
// look at the next line, and see if it begins with an expected fixed
|
|
|
|
// string.
|
|
|
|
|
|
|
|
// Assume there is a next field, it would be silly if there weren't.
|
|
|
|
nextField = *(++StatementDefinition::Iterator(sdit));
|
|
|
|
if( nextField.type() == Field::FixedString )
|
|
|
|
{
|
|
|
|
nextStatement = *(++StatementList::Iterator(sit));
|
|
|
|
newPosition = nextStatement.text().find(TQRegExp("\\b" + nextField.string() + "\\b"));
|
|
|
|
if(newPosition != 0)
|
|
|
|
{
|
|
|
|
// If the next field is optional just carry on as nothing happened,
|
|
|
|
// the next line will be processed as a new statement
|
|
|
|
if(!nextField.compulsory()) continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
position = 0;
|
|
|
|
line = (*(++sit)).text();
|
|
|
|
m_currentSourceLine = (*sit).content;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Field::None):
|
|
|
|
// Do nothing
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( saveToken )
|
|
|
|
fieldMap[field.key()] = OutputField( token );
|
|
|
|
|
|
|
|
if ( saveSingleLine )
|
|
|
|
{
|
|
|
|
SourceLineList list;
|
|
|
|
list << SourceLine( token, 0, -1 );
|
|
|
|
fieldMap[field.key()] = OutputField( list );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( saveBraced )
|
|
|
|
fieldMap[field.key()] = OutputField( (*sit).bracedCode );
|
|
|
|
// If position = -1, we have reached the end of the line.
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if we got to the end of the line, but not all fields had been
|
|
|
|
// processed.
|
|
|
|
if( position != -1 && position <= int(line.length()) )
|
|
|
|
{
|
|
|
|
mistake( Microbe::TooManyTokens );
|
|
|
|
errorInLine = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( errorInLine ) continue;
|
|
|
|
|
|
|
|
// Everything has been parsed up, so send it off for processing.
|
|
|
|
processStatement( command, fieldMap );
|
|
|
|
|
|
|
|
if( showBracesInSource )
|
|
|
|
m_code->append(new Instr_sourceCode("}"));
|
|
|
|
}
|
|
|
|
|
|
|
|
delete m_pPic;
|
|
|
|
return m_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Parser::processAssignment(const TQString &line)
|
|
|
|
{
|
|
|
|
TQStringList tokens = Statement::tokenise(line);
|
|
|
|
|
|
|
|
// Have to have at least 3 tokens for an assignment;
|
|
|
|
if ( tokens.size() < 3 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
TQString firstToken = tokens[0];
|
|
|
|
|
|
|
|
firstToken = mb->alias(firstToken);
|
|
|
|
// Well firstly we look to see if it is a known variable.
|
|
|
|
// These can include 'special' variables such as ports
|
|
|
|
// For now, the processor subclass generates ports it self
|
|
|
|
// and puts them in a list for us.
|
|
|
|
|
|
|
|
|
|
|
|
// Look for port variables first.
|
|
|
|
if ( firstToken.contains(".") )
|
|
|
|
{
|
|
|
|
PortPin portPin = m_pPic->toPortPin( firstToken );
|
|
|
|
|
|
|
|
// check port is valid
|
|
|
|
if ( portPin.pin() == -1 )
|
|
|
|
mistake( Microbe::InvalidPort, firstToken );
|
|
|
|
// more error checking
|
|
|
|
if ( tokens[1] != "=" )
|
|
|
|
mistake( Microbe::UnassignedPin );
|
|
|
|
|
|
|
|
TQString state = tokens[2];
|
|
|
|
if( state == "high" )
|
|
|
|
m_pPic->Ssetlh( portPin, true );
|
|
|
|
else if( state == "low" )
|
|
|
|
m_pPic->Ssetlh( portPin, false );
|
|
|
|
else
|
|
|
|
mistake( Microbe::NonHighLowPinState );
|
|
|
|
}
|
|
|
|
// no dots, lets try for just a port name
|
|
|
|
else if( m_pPic->isValidPort( firstToken ) )
|
|
|
|
{
|
|
|
|
// error checking
|
|
|
|
if ( tokens[1] != "=" )
|
|
|
|
mistake( Microbe::UnassignedPort, tokens[1] );
|
|
|
|
|
|
|
|
Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.find("=")+1));
|
|
|
|
m_pPic->saveResultToVar( firstToken );
|
|
|
|
}
|
|
|
|
else if ( m_pPic->isValidTris( firstToken ) )
|
|
|
|
{
|
|
|
|
if( tokens[1] == "=" )
|
|
|
|
{
|
|
|
|
Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.find("=")+1));
|
|
|
|
m_pPic->Stristate(firstToken);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Is there an assignment?
|
|
|
|
if ( tokens[1] != "=" )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( !mb->isValidVariableName( firstToken ) )
|
|
|
|
{
|
|
|
|
mistake( Microbe::InvalidVariableName, firstToken );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't care whether or not the variable is new; Microbe will only add it if it
|
|
|
|
// hasn't been defined yet.
|
|
|
|
mb->addVariable( Variable( Variable::charType, firstToken ) );
|
|
|
|
|
|
|
|
Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.find("=")+1));
|
|
|
|
|
|
|
|
Variable v = mb->variable( firstToken );
|
|
|
|
switch ( v.type() )
|
|
|
|
{
|
|
|
|
case Variable::charType:
|
|
|
|
m_pPic->saveResultToVar( v.name() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Variable::keypadType:
|
|
|
|
mistake( Microbe::ReadOnlyVariable, v.name() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Variable::sevenSegmentType:
|
|
|
|
m_pPic->SsevenSegment( v );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Variable::invalidType:
|
|
|
|
// Doesn't happen, but include this case to avoid compiler warnings
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SourceLineList Parser::getBracedCode( SourceLineList::const_iterator * it, SourceLineList::const_iterator end )
|
|
|
|
{
|
|
|
|
// Note: The sourceline list has the braces on separate lines.
|
|
|
|
|
|
|
|
// This function should only be called when the parser comes across a line that is a brace.
|
|
|
|
assert( (**it).text() == "{" );
|
|
|
|
|
|
|
|
SourceLineList braced;
|
|
|
|
|
|
|
|
// Jump past the first brace
|
|
|
|
unsigned level = 1;
|
|
|
|
++(*it);
|
|
|
|
|
|
|
|
for ( ; *it != end; ++(*it) )
|
|
|
|
{
|
|
|
|
if ( (**it).text() == "{" )
|
|
|
|
level++;
|
|
|
|
|
|
|
|
else if ( (**it).text() == "}" )
|
|
|
|
level--;
|
|
|
|
|
|
|
|
if ( level == 0 )
|
|
|
|
return braced;
|
|
|
|
|
|
|
|
braced << **it;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO Error: mismatched bracing
|
|
|
|
return braced;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Parser::processStatement( const TQString & name, const OutputFieldMap & fieldMap )
|
|
|
|
{
|
|
|
|
// Name is guaranteed to be something known, the calling
|
|
|
|
// code has taken care of that. Also fieldMap is guaranteed to contain
|
|
|
|
// all required fields.
|
|
|
|
|
|
|
|
if ( name == "goto" )
|
|
|
|
m_pPic->Sgoto(fieldMap["label"].string());
|
|
|
|
|
|
|
|
else if ( name == "call" )
|
|
|
|
m_pPic->Scall(fieldMap["label"].string());
|
|
|
|
|
|
|
|
else if ( name == "while" )
|
|
|
|
m_pPic->Swhile( parseWithChild(fieldMap["code"].bracedCode() ), fieldMap["expression"].string() );
|
|
|
|
|
|
|
|
else if ( name == "repeat" )
|
|
|
|
m_pPic->Srepeat( parseWithChild(fieldMap["code"].bracedCode() ), fieldMap["expression"].string() );
|
|
|
|
|
|
|
|
else if ( name == "if" )
|
|
|
|
m_pPic->Sif(
|
|
|
|
parseWithChild(fieldMap["ifCode"].bracedCode() ),
|
|
|
|
parseWithChild(fieldMap["elseCode"].bracedCode() ),
|
|
|
|
fieldMap["expression"].string() );
|
|
|
|
|
|
|
|
else if ( name == "sub" || name == "subroutine" )
|
|
|
|
{
|
|
|
|
if(!m_bPassedEnd)
|
|
|
|
{
|
|
|
|
mistake( Microbe::InterruptBeforeEnd );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_pPic->Ssubroutine( fieldMap["label"].string(), parseWithChild( fieldMap["code"].bracedCode() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( name == "interrupt" )
|
|
|
|
{
|
|
|
|
TQString interrupt = fieldMap["label"].string();
|
|
|
|
|
|
|
|
if(!m_bPassedEnd)
|
|
|
|
{
|
|
|
|
mistake( Microbe::InterruptBeforeEnd );
|
|
|
|
}
|
|
|
|
else if( !m_pPic->isValidInterrupt( interrupt ) )
|
|
|
|
{
|
|
|
|
mistake( Microbe::InvalidInterrupt );
|
|
|
|
}
|
|
|
|
else if ( mb->isInterruptUsed( interrupt ) )
|
|
|
|
{
|
|
|
|
mistake( Microbe::InterruptRedefined );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mb->setInterruptUsed( interrupt );
|
|
|
|
m_pPic->Sinterrupt( interrupt, parseWithChild( fieldMap["code"].bracedCode() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( name == "end" )
|
|
|
|
{
|
|
|
|
///TODO handle end if we are not in the top level
|
|
|
|
m_bPassedEnd = true;
|
|
|
|
m_pPic->Send();
|
|
|
|
}
|
|
|
|
else if( name == "for" )
|
|
|
|
{
|
|
|
|
TQString step = fieldMap["stepExpression"].string();
|
|
|
|
bool stepPositive;
|
|
|
|
|
|
|
|
if( fieldMap["stepExpression"].found() )
|
|
|
|
{
|
|
|
|
if(step.left(1) == "+")
|
|
|
|
{
|
|
|
|
stepPositive = true;
|
|
|
|
step = step.mid(1).stripWhiteSpace();
|
|
|
|
}
|
|
|
|
else if(step.left(1) == "-")
|
|
|
|
{
|
|
|
|
stepPositive = false;
|
|
|
|
step = step.mid(1).stripWhiteSpace();
|
|
|
|
}
|
|
|
|
else stepPositive = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
step = "1";
|
|
|
|
stepPositive = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString variable = fieldMap["initExpression"].string().mid(0,fieldMap["initExpression"].string().find("=")).stripWhiteSpace();
|
|
|
|
TQString endExpr = variable+ " <= " + fieldMap["toExpression"].string().stripWhiteSpace();
|
|
|
|
|
|
|
|
if( fieldMap["stepExpression"].found() )
|
|
|
|
{
|
|
|
|
bool isConstant;
|
|
|
|
step = processConstant(step,&isConstant);
|
|
|
|
if( !isConstant )
|
|
|
|
mistake( Microbe::NonConstantStep );
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceLineList tempList;
|
|
|
|
tempList << SourceLine( fieldMap["initExpression"].string(), 0, -1 );
|
|
|
|
|
|
|
|
m_pPic->Sfor( parseWithChild( fieldMap["code"].bracedCode() ), parseWithChild( tempList ), endExpr, variable, step, stepPositive );
|
|
|
|
}
|
|
|
|
else if( name == "alias" )
|
|
|
|
{
|
|
|
|
// It is important to get this the right way round!
|
|
|
|
// The alias should be the key since two aliases could
|
|
|
|
// point to the same name.
|
|
|
|
|
|
|
|
TQString alias = fieldMap["alias"].string().stripWhiteSpace();
|
|
|
|
TQString dest = fieldMap["dest"].string().stripWhiteSpace();
|
|
|
|
|
|
|
|
// Check to see whether or not we've already aliased it...
|
|
|
|
// if ( mb->alias(alias) != alias )
|
|
|
|
// mistake( Microbe::AliasRedefined );
|
|
|
|
// else
|
|
|
|
mb->addAlias( alias, dest );
|
|
|
|
}
|
|
|
|
else if( name == "increment" )
|
|
|
|
{
|
|
|
|
TQString variableName = fieldMap["variable"].string();
|
|
|
|
|
|
|
|
if ( !mb->isVariableKnown( variableName ) )
|
|
|
|
mistake( Microbe::UnknownVariable );
|
|
|
|
else if ( !mb->variable( variableName ).isWritable() )
|
|
|
|
mistake( Microbe::ReadOnlyVariable, variableName );
|
|
|
|
else
|
|
|
|
m_pPic->SincVar( variableName );
|
|
|
|
}
|
|
|
|
else if( name == "decrement" )
|
|
|
|
{
|
|
|
|
TQString variableName = fieldMap["variable"].string();
|
|
|
|
|
|
|
|
if ( !mb->isVariableKnown( variableName ) )
|
|
|
|
mistake( Microbe::UnknownVariable );
|
|
|
|
else if ( !mb->variable( variableName ).isWritable() )
|
|
|
|
mistake( Microbe::ReadOnlyVariable, variableName );
|
|
|
|
else
|
|
|
|
m_pPic->SdecVar( variableName );
|
|
|
|
}
|
|
|
|
else if( name == "rotateleft" )
|
|
|
|
{
|
|
|
|
TQString variableName = fieldMap["variable"].string();
|
|
|
|
|
|
|
|
if ( !mb->isVariableKnown( variableName ) )
|
|
|
|
mistake( Microbe::UnknownVariable );
|
|
|
|
else if ( !mb->variable( variableName ).isWritable() )
|
|
|
|
mistake( Microbe::ReadOnlyVariable, variableName );
|
|
|
|
else
|
|
|
|
m_pPic->SrotlVar( variableName );
|
|
|
|
}
|
|
|
|
else if( name == "rotateright" )
|
|
|
|
{
|
|
|
|
TQString variableName = fieldMap["variable"].string();
|
|
|
|
|
|
|
|
if ( !mb->isVariableKnown( variableName ) )
|
|
|
|
mistake( Microbe::UnknownVariable );
|
|
|
|
else if ( !mb->variable( variableName ).isWritable() )
|
|
|
|
mistake( Microbe::ReadOnlyVariable, variableName );
|
|
|
|
else
|
|
|
|
m_pPic->SrotrVar( variableName );
|
|
|
|
}
|
|
|
|
else if( name == "asm" )
|
|
|
|
{
|
|
|
|
m_pPic->Sasm( SourceLine::toStringList( fieldMap["code"].bracedCode() ).join("\n") );
|
|
|
|
}
|
|
|
|
else if( name == "delay" )
|
|
|
|
{
|
|
|
|
// This is one of the rare occasions that the number will be bigger than a byte,
|
|
|
|
// so suppressNumberTooBig must be used
|
|
|
|
bool isConstant;
|
|
|
|
TQString delay = processConstant(fieldMap["expression"].string(),&isConstant,true);
|
|
|
|
if (!isConstant)
|
|
|
|
mistake( Microbe::NonConstantDelay );
|
|
|
|
// else m_pPic->Sdelay( fieldMap["expression"].string(), "");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO We should use the "delay" string returned by processConstant - not the expression (as, e.g. 2*3 won't be ok)
|
|
|
|
int length_ms = literalToInt( fieldMap["expression"].string() );
|
|
|
|
if ( length_ms >= 0 )
|
|
|
|
m_pPic->Sdelay( length_ms * 1000 ); // Pause the delay length in microseconds
|
|
|
|
else
|
|
|
|
mistake( Microbe::NonConstantDelay );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( name == "keypad" || name == "sevenseg" )
|
|
|
|
{
|
|
|
|
TQStringList pins = TQStringList::split( ' ', fieldMap["pinlist"].string() );
|
|
|
|
TQString variableName = fieldMap["name"].string();
|
|
|
|
|
|
|
|
if ( mb->isVariableKnown( variableName ) )
|
|
|
|
{
|
|
|
|
mistake( Microbe::VariableRedefined, variableName );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PortPinList pinList;
|
|
|
|
|
|
|
|
TQStringList::iterator end = pins.end();
|
|
|
|
for ( TQStringList::iterator it = pins.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
PortPin portPin = m_pPic->toPortPin(*it);
|
|
|
|
if ( portPin.pin() == -1 )
|
|
|
|
{
|
|
|
|
// Invalid port/pin
|
|
|
|
//TODO mistake
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pinList << portPin;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( name == "keypad" )
|
|
|
|
{
|
|
|
|
Variable v( Variable::keypadType, variableName );
|
|
|
|
v.setPortPinList( pinList );
|
|
|
|
mb->addVariable( v );
|
|
|
|
}
|
|
|
|
|
|
|
|
else // name == "sevenseg"
|
|
|
|
{
|
|
|
|
if ( pinList.size() != 7 )
|
|
|
|
mistake( Microbe::InvalidPinMapSize );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Variable v( Variable::sevenSegmentType, variableName );
|
|
|
|
v.setPortPinList( pinList );
|
|
|
|
mb->addVariable( v );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Parser::mistake( Microbe::MistakeType type, const TQString & context )
|
|
|
|
{
|
|
|
|
mb->compileError( type, context, m_currentSourceLine );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// static function
|
|
|
|
TQStringList Statement::tokenise(const TQString &line)
|
|
|
|
{
|
|
|
|
TQStringList result;
|
|
|
|
TQString current;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
for(int i = 0; i < int(line.length()); i++)
|
|
|
|
{
|
|
|
|
TQChar nextChar = line[i];
|
|
|
|
if( nextChar.isSpace() )
|
|
|
|
{
|
|
|
|
if( count > 0 )
|
|
|
|
{
|
|
|
|
result.append(current);
|
|
|
|
current = "";
|
|
|
|
count = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( nextChar == '=' )
|
|
|
|
{
|
|
|
|
if( count > 0 ) result.append(current);
|
|
|
|
current = "";
|
|
|
|
count = 0;
|
|
|
|
result.append("=");
|
|
|
|
}
|
|
|
|
else if( nextChar == '{' )
|
|
|
|
{
|
|
|
|
if( count > 0 ) result.append(current);
|
|
|
|
current = "";
|
|
|
|
count = 0;
|
|
|
|
result.append("{");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
current.append(nextChar);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( count > 0 ) result.append(current);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Parser::doArithmetic(int lvalue, int rvalue, Expression::Operation op)
|
|
|
|
{
|
|
|
|
switch(op)
|
|
|
|
{
|
|
|
|
case Expression::noop: return 0;
|
|
|
|
case Expression::addition: return lvalue + rvalue;
|
|
|
|
case Expression::subtraction: return lvalue - rvalue;
|
|
|
|
case Expression::multiplication: return lvalue * rvalue;
|
|
|
|
case Expression::division: return lvalue / rvalue;
|
|
|
|
case Expression::exponent: return lvalue ^ rvalue;
|
|
|
|
case Expression::equals: return lvalue == rvalue;
|
|
|
|
case Expression::notequals: return !(lvalue == rvalue);
|
|
|
|
case Expression::bwand: return lvalue & rvalue;
|
|
|
|
case Expression::bwor: return lvalue | rvalue;
|
|
|
|
case Expression::bwxor: return lvalue ^ rvalue;
|
|
|
|
case Expression::bwnot: return !rvalue;
|
|
|
|
case Expression::le: return lvalue <= rvalue;
|
|
|
|
case Expression::ge: return lvalue >= rvalue;
|
|
|
|
case Expression::lt: return lvalue < rvalue;
|
|
|
|
case Expression::gt: return lvalue > rvalue;
|
|
|
|
|
|
|
|
case Expression::pin:
|
|
|
|
case Expression::notpin:
|
|
|
|
case Expression::function:
|
|
|
|
case Expression::divbyzero:
|
|
|
|
case Expression::read_keypad:
|
|
|
|
// Not applicable actions.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Parser::isLiteral( const TQString &text )
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
literalToInt( text, & ok );
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Literal's in form:
|
|
|
|
-> 321890
|
|
|
|
-> 021348
|
|
|
|
-> 0x3C
|
|
|
|
-> b'0100110'
|
|
|
|
-> 0101001b
|
|
|
|
-> h'43A'
|
|
|
|
-> 2Ah
|
|
|
|
|
|
|
|
Everything else is non-literal...
|
|
|
|
*/
|
|
|
|
int Parser::literalToInt( const TQString &literal, bool * ok )
|
|
|
|
{
|
|
|
|
bool temp;
|
|
|
|
if ( !ok )
|
|
|
|
ok = & temp;
|
|
|
|
*ok = true;
|
|
|
|
|
|
|
|
int value = -1;
|
|
|
|
|
|
|
|
// Note when we use toInt, we don't have to worry about checking
|
|
|
|
// that literal.mid() is convertible, as toInt returns this in ok anyway.
|
|
|
|
|
|
|
|
// Try binary first, of form b'n...n'
|
|
|
|
if( literal.left(2) == "b'" && literal.right(1) == "'" )
|
|
|
|
{
|
|
|
|
value = literal.mid(2,literal.length() - 3).toInt(ok,2);
|
|
|
|
return *ok ? value : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then try hex of form h'n...n'
|
|
|
|
if( literal.left(2) == "h'" && literal.right(1) == "'" )
|
|
|
|
{
|
|
|
|
value = literal.mid(2,literal.length() - 3).toInt(ok,16);
|
|
|
|
return *ok ? value : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, let TQString try and convert it
|
|
|
|
// base 0 == automatic base guessing
|
|
|
|
value = literal.toInt( ok, 0 );
|
|
|
|
return *ok ? value : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Parser::compileConditionalExpression( const TQString & expression, Code * ifCode, Code * elseCode ) const
|
|
|
|
{
|
|
|
|
///HACK ///TODO this is a little improper, I don't think we should be using the pic that called us...
|
|
|
|
|
|
|
|
Expression( m_pPic, mb, m_currentSourceLine, false ).compileConditional(expression,ifCode,elseCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString Parser::processConstant(const TQString &expression, bool * isConstant, bool suppressNumberTooBig) const
|
|
|
|
{
|
|
|
|
return Expression( m_pPic, mb, m_currentSourceLine, suppressNumberTooBig ).processConstant(expression, isConstant);
|
|
|
|
}
|
|
|
|
//END class Parser
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN class Field
|
|
|
|
Field::Field()
|
|
|
|
{
|
|
|
|
m_type = None;
|
|
|
|
m_compulsory = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Field::Field( Type type, const TQString & key )
|
|
|
|
{
|
|
|
|
m_type = type;
|
|
|
|
m_compulsory = false;
|
|
|
|
m_key = key;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Field::Field( Type type, const TQString & key, const TQString & string, bool compulsory )
|
|
|
|
{
|
|
|
|
m_type = type;
|
|
|
|
m_compulsory = compulsory;
|
|
|
|
m_key = key;
|
|
|
|
m_string = string;
|
|
|
|
}
|
|
|
|
//END class Field
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN class OutputField
|
|
|
|
OutputField::OutputField()
|
|
|
|
{
|
|
|
|
m_found = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
OutputField::OutputField( const SourceLineList & bracedCode )
|
|
|
|
{
|
|
|
|
m_bracedCode = bracedCode;
|
|
|
|
m_found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
OutputField::OutputField( const TQString & string/*, int lineNumber*/ )
|
|
|
|
{
|
|
|
|
m_string = string;
|
|
|
|
m_found = true;
|
|
|
|
}
|
|
|
|
//END class OutputField
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// Second pass
|
|
|
|
|
|
|
|
else if( firstToken == "include" )
|
|
|
|
{
|
|
|
|
// only cope with 'sane' strings a.t.m.
|
|
|
|
// e.g. include "filename.extenstion"
|
|
|
|
TQString filename = (*sit).content.mid( (*sit).content.find("\"") ).stripWhiteSpace();
|
|
|
|
// don't strip whitespace from within quotes as you
|
|
|
|
// can have filenames composed entirely of spaces (kind of weird)...
|
|
|
|
// remove quotes.
|
|
|
|
filename = filename.mid(1);
|
|
|
|
filename = filename.mid(0,filename.length()-1);
|
|
|
|
TQFile includeFile(filename);
|
|
|
|
if( includeFile.open(IO_ReadOnly) )
|
|
|
|
{
|
|
|
|
TQTextStream stream( &includeFile );
|
|
|
|
TQStringList includeCode;
|
|
|
|
while( !stream.atEnd() )
|
|
|
|
{
|
|
|
|
includeCode += stream.readLine();
|
|
|
|
}
|
|
|
|
///TODO make includes work
|
|
|
|
//output += parse(includeCode);
|
|
|
|
includeFile.close();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
mistake( Microbe::UnopenableInclude, filename );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|