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

1055 lines
28 KiB

/***************************************************************************
* 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").tqarg( (*sit).content.line() + 1 ).tqarg( (*sit).content.url() ).tqarg( (*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