summaryrefslogtreecommitdiffstats
path: root/microbe/parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'microbe/parser.cpp')
-rw-r--r--microbe/parser.cpp1054
1 files changed, 1054 insertions, 0 deletions
diff --git a/microbe/parser.cpp b/microbe/parser.cpp
new file mode 100644
index 0000000..15335e0
--- /dev/null
+++ b/microbe/parser.cpp
@@ -0,0 +1,1054 @@
+/***************************************************************************
+ * 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 <qfile.h>
+#include <qregexp.h>
+#include <qstring.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;
+
+ QString line = (*sit).text();
+
+ QString 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( QString("#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() )
+ {
+ QString 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);
+ QString 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(QRegExp("\\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(QRegExp("\\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(QRegExp("\\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 QString &line)
+{
+ QStringList tokens = Statement::tokenise(line);
+
+ // Have to have at least 3 tokens for an assignment;
+ if ( tokens.size() < 3 )
+ return false;
+
+ QString 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 );
+
+ QString 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 QString & 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" )
+ {
+ QString 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" )
+ {
+ QString 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;
+ }
+
+ QString variable = fieldMap["initExpression"].string().mid(0,fieldMap["initExpression"].string().find("=")).stripWhiteSpace();
+ QString 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.
+
+ QString alias = fieldMap["alias"].string().stripWhiteSpace();
+ QString 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" )
+ {
+ QString 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" )
+ {
+ QString 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" )
+ {
+ QString 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" )
+ {
+ QString 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;
+ QString 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" )
+ {
+ QStringList pins = QStringList::split( ' ', fieldMap["pinlist"].string() );
+ QString variableName = fieldMap["name"].string();
+
+ if ( mb->isVariableKnown( variableName ) )
+ {
+ mistake( Microbe::VariableRedefined, variableName );
+ return;
+ }
+
+ PortPinList pinList;
+
+ QStringList::iterator end = pins.end();
+ for ( QStringList::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 QString & context )
+{
+ mb->compileError( type, context, m_currentSourceLine );
+}
+
+
+// static function
+QStringList Statement::tokenise(const QString &line)
+{
+ QStringList result;
+ QString current;
+ int count = 0;
+
+ for(int i = 0; i < int(line.length()); i++)
+ {
+ QChar 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 QString &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 QString &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 QString try and convert it
+ // base 0 == automatic base guessing
+ value = literal.toInt( ok, 0 );
+ return *ok ? value : -1;
+}
+
+
+void Parser::compileConditionalExpression( const QString & 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);
+}
+
+
+QString Parser::processConstant(const QString &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 QString & key )
+{
+ m_type = type;
+ m_compulsory = false;
+ m_key = key;
+}
+
+
+Field::Field( Type type, const QString & key, const QString & 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 QString & 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"
+ QString 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);
+ QFile includeFile(filename);
+ if( includeFile.open(IO_ReadOnly) )
+ {
+ QTextStream stream( &includeFile );
+ QStringList includeCode;
+ while( !stream.atEnd() )
+ {
+ includeCode += stream.readLine();
+ }
+ ///TODO make includes work
+ //output += parse(includeCode);
+ includeFile.close();
+ }
+ else
+ mistake( Microbe::UnopenableInclude, filename );
+ }
+#endif
+
+