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/expression.cpp

843 lines
21 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 "btreenode.h"
#include "expression.h"
#include "traverser.h"
#include "parser.h"
#include "pic14.h"
#include <kdebug.h>
#include <klocale.h>
#include <tqregexp.h>
Expression::Expression( PIC14 *pic, Microbe *master, SourceLine sourceLine, bool suppressNumberTooBig )
: m_sourceLine(sourceLine)
{
m_pic = pic;
mb = master;
m_bSupressNumberTooBig = suppressNumberTooBig;
}
Expression::~Expression()
{
}
void Expression::traverseTree( BTreeNode *root, bool conditionalRoot )
{
Traverser t(root);
t.start();
// special case: if we are starting at the root node then
// we are dealing with something of the form variable = 6
// or variable = portb
///TODO reimplement assignments as two branched trees?
if ( t.current() == root &&
!root->hasChildren() &&
t.current()->childOp() != pin &&
t.current()->childOp() != notpin &&
t.current()->childOp() != function &&
t.current()->childOp() != read_keypad )
{
switch(root->type())
{
case number: m_pic->assignNum(root->value()); break;
case variable: m_pic->assignVar(root->value()); break;
default: break; // Should never get here
}
// no need to traverse the tree as there is none.
return;
}
t.setCurrent(root);
if(t.current()->hasChildren())
{
// Here we work out what needs evaulating, and in which order.
// To minimize register usage, if only one branch needs traversing,
// then that branch should be done first.
bool evaluateLeft = t.current()->left()->needsEvaluating();
BTreeNode *evaluateFirst;
BTreeNode *evaluateSecond;
// If both need doing, then it really doesn't matter which we do
// first (unless we are looking to do really complex optimizations...
// Cases:
// - Both need evaluating,
// - or left needs doing first,
// in both cases we evaluate left, then right.
if( evaluateLeft )
{
evaluateFirst = t.current()->left();
evaluateSecond = t.current()->right();
}
// Otherwise it is best to evaluate right first for reasons given above.
else
{
evaluateFirst = t.current()->right();
evaluateSecond = t.current()->left();
}
TQString dest1 = mb->dest();
mb->incDest();
TQString dest2 = mb->dest();
mb->decDest();
bool evaluated = false;
if( evaluateFirst->hasChildren() )
{
traverseTree(evaluateFirst);
evaluated = true;
}
else if( isUnaryOp(evaluateFirst->childOp()) )
{
doUnaryOp( evaluateFirst->childOp(), evaluateFirst );
evaluated = true;
}
if ( evaluated )
{
// We need to save the result if we are going tro traverse the other
// branch, or if we are performing a subtraction in which case the
// value wanted in working is not the current value.
// But as the optimizer will deal with unnecessary variables anyway,
// always save to a register
evaluateFirst->setReg( dest1 );
evaluateFirst->setType( variable );
m_pic->saveToReg( dest1 );
}
evaluated = false;
if( evaluateSecond->hasChildren() )
{
mb->incDest();
mb->incDest();
traverseTree(evaluateSecond);
evaluated = true;
mb->decDest();
mb->decDest();
}
else if( isUnaryOp(evaluateSecond->childOp()) )
{
doUnaryOp( evaluateSecond->childOp(), evaluateSecond );
evaluated = true;
}
if ( evaluated )
{
evaluateSecond->setReg( dest2 );
evaluateSecond->setType( variable );
m_pic->saveToReg( dest2 );
}
}
if(t.current()->childOp()==divbyzero)
{
mistake( Microbe::DivisionByZero );
}
// If we are at the top level of something like 'if a == 3 then', then we are ready to put
// in the if code, else the expression just evaluates to 0 or 1
if(conditionalRoot && t.current() == root)
m_pic->setConditionalCode(m_ifCode, m_elseCode);
// Handle operations
// (functions are not actually supported)
if(isUnaryOp(t.current()->childOp()))
doUnaryOp( t.current()->childOp(), t.current() );
else
doOp( t.current()->childOp(), t.current()->left(), t.current()->right() );
}
void Expression::doOp( Operation op, BTreeNode *left, BTreeNode *right )
{
TQString lvalue;
if(left->reg().isEmpty())
lvalue = left->value();
else
lvalue = left->reg();
TQString rvalue;
if(right->reg().isEmpty())
rvalue = right->value();
else
rvalue = right->reg();
// Handle if stuff
PIC14::LocationType leftType;
switch ( left->type() )
{
case number:
leftType = PIC14::num;
break;
case variable:
leftType = PIC14::var;
break;
case working:
leftType = PIC14::work;
break;
case unset:
case extpin:
case keypad:
kdError() << k_funcinfo << "Bad left->type(): " << left->type() << endl;
};
PIC14::LocationType rightType;
switch ( right->type() )
{
case number:
rightType = PIC14::num;
break;
case variable:
rightType = PIC14::var;
break;
case working:
rightType = PIC14::work;
break;
case unset:
case extpin:
case keypad:
kdError() << k_funcinfo << "Bad right->type(): " << right->type() << endl;
};
switch(op)
{
case equals: m_pic->equal( lvalue, rvalue, leftType, rightType ); break;
case notequals: m_pic->notEqual( lvalue, rvalue, leftType, rightType ); break;
case lt: m_pic->lessThan( lvalue, rvalue, leftType, rightType ); break;
case gt: m_pic->greaterThan( lvalue, rvalue, leftType, rightType ); break;
case le: m_pic->lessOrEqual( lvalue, rvalue, leftType, rightType ); break;
case ge: m_pic->greaterOrEqual( lvalue, rvalue, leftType, rightType ); break;
case addition: m_pic->add( lvalue, rvalue, leftType, rightType ); break;
case subtraction: m_pic->subtract( lvalue, rvalue, leftType, rightType ); break;
case multiplication: m_pic->mul( lvalue, rvalue, leftType, rightType ); break;
case division: m_pic->div( lvalue, rvalue, leftType, rightType ); break;
case bwand: m_pic->bitwise( bwand, lvalue, rvalue, leftType, rightType ); break;
case bwor: m_pic->bitwise( bwor, lvalue, rvalue, leftType, rightType ); break;
case bwxor: m_pic->bitwise( bwxor, lvalue, rvalue, leftType, rightType ); break;
case bwnot: m_pic->bitwise( bwnot, lvalue, rvalue, leftType, rightType ); break;
default: break;
}
}
void Expression::buildTree( const TQString & unstrippedExpression, BTreeBase *tree, BTreeNode *node, int level )
{
int firstEnd = 0;
int secondStart = 0;
bool unary = false;
Operation op;
TQString expression = stripBrackets( unstrippedExpression );
switch(level)
{
// ==, !=
case 0:
{
int equpos = findSkipBrackets(expression, "==");
int neqpos = findSkipBrackets(expression, "!=");
if( equpos != -1 )
{
op = equals;
firstEnd = equpos;
secondStart = equpos + 2;
}
else if( neqpos != -1 )
{
op = notequals;
firstEnd = neqpos;
secondStart = neqpos + 2;
}
else op = noop;
break;
}
// <, <=, >=, >
case 1:
{
int ltpos = findSkipBrackets(expression, "<");
int lepos = findSkipBrackets(expression, "<=");
int gepos = findSkipBrackets(expression, ">=");
int gtpos = findSkipBrackets(expression, ">");
// Note: if (for example) "<=" is present, "<" will also be present. This
// means that we have to check for "<=" before "<", etc.
if( lepos != -1 )
{
op = le;
firstEnd = lepos;
secondStart = lepos + 2;
}
else if( gepos != -1 )
{
op = ge;
firstEnd = gepos;
secondStart = gepos + 2;
}
else if( ltpos != -1 )
{
op = lt;
firstEnd = ltpos;
secondStart = ltpos + 1;
}
else if( gtpos != -1 )
{
op = gt;
firstEnd = gtpos;
secondStart = gtpos + 1;
}
else op = noop;
break;
}
// +,-
case 2:
{
int addpos = findSkipBrackets(expression, '+');
int subpos = findSkipBrackets(expression, '-');
if( subpos != -1 )
{
op = subtraction;
firstEnd = subpos;
secondStart = subpos + 1;
}
else if( addpos != -1 )
{
op = addition;
firstEnd = addpos;
secondStart = addpos + 1;
}
else op = noop;
break;
}
// *,/
case 3:
{
int mulpos = findSkipBrackets(expression, '*');
int divpos = findSkipBrackets(expression, '/');
if( divpos != -1 )
{
op = division;
firstEnd = divpos;
secondStart = divpos + 1;
}
else if( mulpos != -1 )
{
op = multiplication;
firstEnd = mulpos;
secondStart = mulpos + 1;
}
else op = noop;
break;
}
// ^
case 4:
{
int exppos = findSkipBrackets(expression, '^');
if( exppos != -1 )
{
op = exponent;
firstEnd = exppos;
secondStart = exppos + 1;
}
else op = noop;
break;
}
// AND, OR, XOR
case 5:
{
int bwAndPos = findSkipBrackets(expression, " AND ");
int bwOrPos = findSkipBrackets(expression, " OR ");
int bwXorPos = findSkipBrackets(expression, " XOR ");
if( bwAndPos != -1 )
{
op = bwand;
firstEnd = bwAndPos;
secondStart = bwAndPos + 5;
}
else if( bwOrPos != -1 )
{
op = bwor;
firstEnd = bwOrPos;
secondStart = bwOrPos + 4;
}
else if( bwXorPos != -1 )
{
op = bwxor;
firstEnd = bwXorPos;
secondStart = bwXorPos + 5;
}
else op = noop;
break;
}
// NOT
case 6:
{
int bwNotPos = findSkipBrackets(expression, " NOT ");
if( bwNotPos != -1 )
{
op = bwnot;
unary = true;
firstEnd = bwNotPos; // this line is not needed for unary things/
secondStart = bwNotPos + 5;
}
else op = noop;
break;
}
}
node->setChildOp(op);
TQString tokens[2];
tokens[0] = expression.left(firstEnd).stripWhiteSpace();
tokens[1] = expression.mid(secondStart).stripWhiteSpace();
if( op != noop )
{
for( int j = 0; j < 2; j++ )
{
BTreeNode *newNode = new BTreeNode();
tree->addNode( node, newNode, (j == 0) );
// we need to strip any brackets from the sub-expression
// try each token again at the same level, if they
// don't have any of this level's operators, then the function
// will go to the next level as below.
// For unary opertaions, e.g NOT, we have no special
// code for nodes with only one child, so we leave the left
// hand child blank and put the rest in the right hand node.
if( unary && j == 0 )
{
newNode->setValue("");
newNode->setType(number);
}
else buildTree(tokens[j], tree, newNode, 0 );
}
}
else
{
// if there was no relevant operation i.e. " 3*4 / 6" as opposed to " 3*4 + 6"
// then just pass the node onto the next parsing level.
// unless we are at the lowest level, in which case we have reached a final value.
if( level == 6 ) expressionValue(expression,tree,node);
else
{
buildTree(expression,tree,node,level + 1);
}
}
}
void Expression::doUnaryOp(Operation op, BTreeNode *node)
{
/* Note that this isn't for unary operations as such,
rather for things that are operations that have no direct tqchildren,
e.g. portx.n is high, and functionname(args)*/
if ( op == pin || op == notpin )
m_pic->Spin( m_pic->toPortPin( node->value() ), (op==notpin) );
else if ( op == read_keypad )
m_pic->Skeypad( mb->variable( node->value() ) );
}
void Expression::compileExpression( const TQString & expression )
{
// Make a tree to put the expression in.
BTreeBase *tree = new BTreeBase();
BTreeNode *root = new BTreeNode();
// parse the expression into the tree
buildTree(expression,tree,root,0);
// compile the tree into assembly code
tree->setRoot(root);
tree->pruneTree(tree->root());
traverseTree(tree->root());
// Note deleting the tree deletes all nodes, so the root
// doesn't need deleting separately.
delete tree;
return;
}
void Expression::compileConditional( const TQString & expression, Code * ifCode, Code * elseCode )
{
if( expression.contains(TQRegExp("=>|=<|=!")) )
{
mistake( Microbe::InvalidComparison, expression );
return;
}
if( expression.contains(TQRegExp("[^=><!][=][^=]")))
{
mistake( Microbe::InvalidEquals );
return;
}
// Make a tree to put the expression in.
BTreeBase *tree = new BTreeBase();
BTreeNode *root = new BTreeNode();
// parse the expression into the tree
buildTree(expression,tree,root,0);
// Modify the tree so it is always at the top level of the form (kwoerpkwoep) == (qwopekqpowekp)
if ( root->childOp() != equals &&
root->childOp() != notequals &&
root->childOp() != gt &&
root->childOp() != lt &&
root->childOp() != ge &&
root->childOp() != le &&
root->childOp() != pin &&
root->childOp() != notpin &&
root->childOp() != read_keypad )
{
BTreeNode *newRoot = new BTreeNode();
BTreeNode *oneNode = new BTreeNode();
oneNode->setChildOp(noop);
oneNode->setType(number);
oneNode->setValue("1");
newRoot->setLeft(root);
newRoot->setRight(oneNode);
newRoot->setType(unset);
newRoot->setChildOp(ge);
tree->setRoot(newRoot);
root = newRoot;
}
// compile the tree into assembly code
tree->setRoot(root);
tree->pruneTree(tree->root(),true);
// We might have just a constant expression, in which case we can just always do if or else depending
// on whether it is true or false.
if( root->childOp() == noop )
{
if( root->value().toInt() == 0 )
m_pic->mergeCode( elseCode );
else
m_pic->mergeCode( ifCode );
return;
}
// traverse tree with argument conditionalRoot true
// so that 3 == x gets integrated with code for if, repeat until etc...
m_ifCode = ifCode;
m_elseCode = elseCode;
traverseTree(tree->root(),true);
// Note deleting the tree deletes all nodes, so the root
// doesn't need deleting separately.
delete tree;
}
bool Expression::isUnaryOp(Operation op)
{
return op == pin || op == notpin || op == function || op == read_keypad;
}
void Expression::mistake( Microbe::MistakeType type, const TQString & context )
{
mb->compileError( type, context, m_sourceLine );
}
int Expression::findSkipBrackets( const TQString & expr, char ch, int startPos)
{
bool found = false;
int i = startPos;
int bracketLevel = 0;
while(!found)
{
if(expr[i].latin1() == '\'')
{
if( i + 2 < int(expr.length()) )
{
if( expr[i+2].latin1() == '\'' )
{
i = i + 2;
found = true;
}
}
}
if(expr[i].latin1() == '(') bracketLevel++;
else if(expr[i].latin1() == ')') bracketLevel--;
if( bracketLevel == 0 )
{
if(expr[i].latin1() == ch) found = true;
else i++;
}
else i++;
if( i >= int(expr.length()) )
{
found = true;
i = -1;
}
}
return i;
}
int Expression::findSkipBrackets( const TQString & expr, TQString phrase, int startPos)
{
bool found = false;
int i = startPos;
int bracketLevel = 0;
while(!found)
{
if(expr[i].latin1() == '\'')
{
if( i + 2 < int(expr.length()) )
{
if( expr[i+2].latin1() == '\'' )
{
i = i + 2;
found = true;
}
}
}
if(expr[i].latin1() == '(') bracketLevel++;
else if(expr[i].latin1() == ')') bracketLevel--;
if( bracketLevel == 0 )
{
if(expr.mid(i,phrase.length()) == phrase) found = true;
else i++;
}
else i++;
if( i >= int(expr.length()) )
{
found = true;
i = -1;
}
}
return i;
}
TQString Expression::stripBrackets( TQString expression )
{
bool stripping = true;
int bracketLevel = 0;
int i = 0;
expression = expression.stripWhiteSpace();
while(stripping)
{
if( expression.at(i) == '(' ) bracketLevel++;
else if( expression.at(i) == ')' )
{
if( i == int(expression.length() - 1) && bracketLevel == 1)
{
expression = expression.mid(1,expression.length() - 2).stripWhiteSpace();
}
bracketLevel--;
}
if( i == int(expression.length() - 1) && bracketLevel > 0 )
{
mistake( Microbe::MismatchedBrackets, expression );
// Stray brackets might cause the expressionession parser some problems,
// so we just avoid parsing anything altogether
expression = "";
stripping = false;
}
i++;
if( bracketLevel == 0 ) stripping = false;
}
return expression;
}
void Expression::expressionValue( TQString expr, BTreeBase */*tree*/, BTreeNode *node)
{
/* The "end of the line" for the expression parsing, the
expression has been broken down into the fundamental elements of expr.value()=="to"||
variable, number, special etc... so we now just set value and type */
/* Alternatively we might have a function call
e.g. somefunction(3,potatoes,hairstyle + 6)
In which case we need to call back to parseExpr to process the arguments,
saving them on the basic stack then making the function call.
Of course we also need to mark the terminal node type as a function.
*/
expr = expr.stripWhiteSpace();
// My intention is so that these error checks are ordered
// so that e.g. for x = 3 it picks up the = rather than the spaces first.
expr = mb->alias(expr);
ExprType t = expressionType(expr);
// See if it is a single qouted character, e.g. 'A'
if( expr.left(1) == "\'" && expr.right(1) == "\'" )
{
if( expr.length() == 3 ) // fall through to report as unknown variable if not of form 'x'
{
// If so, turn it into a number, and use the ASCII code as the value
t = number;
expr = TQString::number(expr[1].latin1());
}
}
// Check for the most common mistake ever!
if(expr.contains("="))
mistake( Microbe::InvalidEquals );
// Check for reserved keywords
if(expr=="to"||expr=="step"||expr=="then")
mistake( Microbe::ReservedKeyword, expr );
// Check for empty expressions, or expressions contating spaces
// both indicating a Mistake.
if(expr.isEmpty())
mistake( Microbe::ConsecutiveOperators );
else if(expr.contains(TQRegExp("\\s")) && t!= extpin)
mistake( Microbe::MissingOperator );
if( t == variable && !mb->isVariableKnown(expr) && !m_pic->isValidPort( expr ) && !m_pic->isValidTris( expr ) )
mistake( Microbe::UnknownVariable, expr );
if ( mb->isVariableKnown(expr) && !mb->variable(expr).isReadable() )
mistake( Microbe::WriteOnlyVariable, expr );
node->setType(t);
// Since we currently only implement 8 bit unsigned integers, we should disallow
// anything outside the range [0-255].
if( t == number && !m_bSupressNumberTooBig && (expr.toInt() > 255) )
{
mistake( Microbe::NumberTooBig );
}
// if there was a pin, we need to decocde it.
// For now and sacrificing syntax error checking
// we just look for the word "is" then "high" or "low".
if( t == extpin )
{
bool NOT;
int i = expr.find("is");
if(i > 0)
{
NOT = expr.contains("low");
if(!expr.contains("high") && !expr.contains("low"))
mistake( Microbe::HighLowExpected, expr );
expr = expr.left(i-1);
}
else NOT = false;
node->setChildOp(NOT?notpin:pin);
}
else if ( t == keypad )
node->setChildOp( read_keypad );
node->setValue(expr);
}
ExprType Expression::expressionType( const TQString & expression )
{
// So we can't handle complex expressions yet anyway,
// let's just decide whether it is a variable or number.
// Thanks to the convention that variable names must not
// begin with a number this is extremely simple to do!
/* But now there is a catch, because there can also be
things that have a first character alpha, but are of the form
"portb.3 is high", general syntax: portx.n is <high|low>
additionally, there can be things that are just porta.6, which just return the truth of that port.
In reality it is just:
portx.n is high === portx.n
portx.n is low === !(portx.n)
These types of expression can be identified by the fact
that they should be the only things that contain a '.'
*/
/* Note that at the moment, literalToInt returns -1 if it is
not literal so isLiteral is redundant, but this may change if say
negative numbers are implemented
*/
int value = Parser::literalToInt(expression);
if ( value != -1 )
return number;
if( expression.contains('.') )
return extpin;
if ( mb->variable( expression ).type() == Variable::keypadType )
return keypad;
return variable;
}
TQString Expression::processConstant( const TQString & expr, bool * isConstant )
{
bool temp;
if (!isConstant)
isConstant = &temp;
TQString code;
// Make a tree to put the expression in.
BTreeBase *tree = new BTreeBase();
BTreeNode *root = new BTreeNode();
// parse the expression into the tree
buildTree(expr,tree,root,0);
// compile the tree into assembly code
tree->setRoot(root);
tree->pruneTree(tree->root());
//code = traverseTree(tree->root());
// Look to see if it is a number
if( root->type() == number )
{
code = root->value();
*isConstant = true;
}
else
{
code = "";
*isConstant = false;
}
// Note deleting the tree deletes all nodes, so the root
// doesn't need deleting separately.
delete tree;
return code;
}