You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1085 lines
26 KiB
C++
1085 lines
26 KiB
C++
/*
|
|
Copyright (C) 2003 by Walter Schreppers
|
|
Copyright (C) 2004 by Cies Breijs
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of version 2 of the GNU General Public
|
|
License as published by the Free Software Foundation.
|
|
|
|
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.
|
|
*/
|
|
|
|
// This file is originally written by Walter Scheppers, but allmost
|
|
// allmost every aspect of it is heavily changed by Cies Breijs.
|
|
|
|
|
|
#include <tqstringlist.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <tdelocale.h>
|
|
|
|
#include "parser.h"
|
|
|
|
|
|
Parser::Parser(TQTextIStream& in)
|
|
{
|
|
lexer = new Lexer(in);
|
|
tree = new TreeNode();
|
|
}
|
|
|
|
Parser::~Parser()
|
|
{
|
|
delete lexer;
|
|
}
|
|
|
|
void Parser::parse()
|
|
{
|
|
tree = Program(); // the first node that keeps the lexer running till finished/error
|
|
}
|
|
|
|
void Parser::getToken()
|
|
{
|
|
currentToken = lexer->lex(); // stores a Token, obtained though the lexer, in 'currentToken'
|
|
row = currentToken.start.row; // these will have to leave eventually, all should be passed on by the Token
|
|
col = currentToken.start.col;
|
|
kdDebug(0)<<"Parser::getToken(), got a token: '"<<currentToken.look<<"', @ ("<<currentToken.start.row<<", "<<currentToken.start.col<<") - ("<<currentToken.end.row<<", "<<currentToken.end.col<<"), tok-number:"<<currentToken.type<<endl;
|
|
}
|
|
|
|
TreeNode* Parser::Program()
|
|
{
|
|
Token emptyToken;
|
|
emptyToken.type = tokNotSet;
|
|
emptyToken.look = "";
|
|
emptyToken.start.row = 0;
|
|
emptyToken.start.col = 0;
|
|
emptyToken.end.row = 0;
|
|
emptyToken.end.col = 0;
|
|
|
|
TreeNode* program = new TreeNode(emptyToken, programNode, "program");
|
|
TreeNode* block = new TreeNode(emptyToken, blockNode, "block");
|
|
|
|
getToken();
|
|
|
|
// this is the main parse loop
|
|
kdDebug(0)<<"Parser::Program(): entering main parse loop..."<<endl;
|
|
while (currentToken.type != tokEOF) // currentToken.type returns the type of the currentToken
|
|
{
|
|
kdDebug(0)<<"Parser::Program(), [main parse loop]: looking for next statement..."<<endl;
|
|
block->appendChild( Statement() );
|
|
while (currentToken.type == tokEOL) getToken(); // newlines between statements are allowed
|
|
// runs statement related code, stores the returned TreeNode* in the nodetree
|
|
// note: Statement() allways gets a new Token with getToken() before it returns
|
|
}
|
|
program->appendChild(block);
|
|
kdDebug(0)<<"Parser::Program(): leaving main parse loop..."<<endl;
|
|
|
|
return program;
|
|
}
|
|
|
|
void Parser::matchToken(int expectedToken)
|
|
{
|
|
if (currentToken.type == expectedToken)
|
|
{
|
|
getToken(); // get a new token
|
|
return;
|
|
}
|
|
|
|
switch (expectedToken)
|
|
{
|
|
case tokEOL:
|
|
Error(currentToken, i18n("Unexpected intruction after the '%1' command, please use only one instruction per line").arg(preservedToken.look), 1010);
|
|
break;
|
|
|
|
case tokBegin:
|
|
Error(preservedToken, i18n("Expected '['"), 1010);
|
|
break;
|
|
|
|
case tokTo:
|
|
Error(currentToken, i18n("Expected 'to' after the '%1' command").arg(preservedToken.look), 1010);
|
|
break;
|
|
|
|
case tokAssign:
|
|
Error(currentToken, i18n("Expected '=' after the '%1' command").arg(preservedToken.look), 1010);
|
|
break;
|
|
|
|
case tokEnd:
|
|
Error(currentToken, i18n("Expected ']' after the '%1' command").arg(preservedToken.look), 1010);
|
|
break;
|
|
|
|
case tokUnknown:
|
|
Error(preservedToken, i18n("Expected a name after the '%1' command").arg(preservedToken.look), 1010);
|
|
break;
|
|
|
|
default:
|
|
Error(currentToken, i18n("UNDEFINED ERROR NR %1: please send this Logo script to the KTurtle developers").arg(expectedToken), 1010);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void Parser::appendParameters(TreeNode* node)
|
|
{
|
|
node->appendChild( Expression() ); // append the first papameter
|
|
while (currentToken.type == tokComma)
|
|
{
|
|
matchToken(tokComma); // push through the comma
|
|
if (currentToken.type == tokEOL) return; // catch forgotten expressions, like "go 10, "
|
|
node->appendChild( Expression() );
|
|
}
|
|
}
|
|
|
|
|
|
TreeNode* Parser::getId()
|
|
{
|
|
TreeNode* n = new TreeNode(currentToken, idNode);
|
|
n->setLook(currentToken.look);
|
|
matchToken(tokUnknown); // Id's are ofcouse not yet known
|
|
return n;
|
|
}
|
|
|
|
|
|
|
|
TreeNode* Parser::FunctionCall(Token maybeFunctionCall)
|
|
{
|
|
kdDebug(0)<<"Parser::FunctionCall() [using identifier: '"<<maybeFunctionCall.look<<"']"<<endl;
|
|
TreeNode* fcall = new TreeNode(maybeFunctionCall, functionCallNode);
|
|
|
|
TreeNode* paramList = new TreeNode(currentToken, idListNode, "idlist");
|
|
// if (currentToken.type != tokEOL && currentToken.type != tokEOF)
|
|
if (currentToken.type == tokNumber ||
|
|
currentToken.type == tokString ||
|
|
currentToken.type == tokUnknown) // only if there is a possible parameter given after the call...
|
|
{
|
|
TreeNode* expr = Expression();
|
|
if (expr->getType() == Unknown) Error(currentToken, i18n("Expected an expression"), 1020);
|
|
else paramList->appendChild(expr);
|
|
while (currentToken.type == tokComma)
|
|
{
|
|
matchToken(tokComma);
|
|
expr = Expression();
|
|
if (expr->getType() == Unknown) Error(currentToken, i18n("Expected an expression"), 1020);
|
|
else paramList->appendChild(expr);
|
|
}
|
|
}
|
|
fcall->appendChild(paramList);
|
|
|
|
return fcall;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::Factor()
|
|
{
|
|
TreeNode* node;
|
|
Token rememberedToken = currentToken;
|
|
switch (currentToken.type)
|
|
{
|
|
case tokBraceOpen:
|
|
matchToken(tokBraceOpen);
|
|
node = Expression();
|
|
matchToken(tokBraceClose);
|
|
break;
|
|
|
|
case tokUnknown:
|
|
node = getId();
|
|
if (learnedFunctionList.contains(rememberedToken.look) > 0) // is function call
|
|
{
|
|
delete node;
|
|
node = FunctionCall(rememberedToken);
|
|
node->setType(funcReturnNode); // expect returned value on stack
|
|
}
|
|
break;
|
|
|
|
case tokString:
|
|
node = new TreeNode(currentToken, constantNode);
|
|
{ // extra scope to localize the TQString 'str'
|
|
TQString str = currentToken.look;
|
|
if ( currentToken.look.endsWith("\"") )
|
|
{
|
|
// cut off the quotes and store the value
|
|
str.remove(0, 1).truncate( currentToken.look.length() - 2 );
|
|
}
|
|
else // problems but we need to keep it moving
|
|
{
|
|
str.remove(0, 1); // cut off the first quote only
|
|
Error(currentToken, i18n("String text not properly delimited with a ' \" ' (double quote)"), 1060);
|
|
}
|
|
node->setValue(str);
|
|
}
|
|
matchToken(tokString);
|
|
break;
|
|
|
|
case tokNumber:
|
|
node = new TreeNode(currentToken, constantNode);
|
|
node->setValue(currentToken.value);
|
|
matchToken(tokNumber);
|
|
break;
|
|
|
|
case tokRun:
|
|
node = ExternalRun();
|
|
break;
|
|
|
|
case tokInputWindow:
|
|
node = InputWindow();
|
|
break;
|
|
|
|
case tokRandom:
|
|
node = Random();
|
|
break;
|
|
|
|
case tokEOL:
|
|
node = new TreeNode(currentToken, Unknown);
|
|
break;
|
|
|
|
default:
|
|
TQString s = currentToken.look;
|
|
if ( s.isEmpty() || currentToken.type == tokEOF )
|
|
{
|
|
Error(currentToken, i18n("INTERNAL ERROR NR %1: please sent this Logo script to KTurtle developers").arg(1), 1020);
|
|
// if this error occurs the see the Parser::Repeat for the good solution using 'preservedToken'
|
|
}
|
|
else
|
|
{
|
|
Error(currentToken, i18n("Cannot understand '%1', expected an expression after the '%2' command").arg(s).arg(preservedToken.look), 1020);
|
|
}
|
|
node = new TreeNode(currentToken, Unknown);
|
|
getToken();
|
|
break;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::signedFactor()
|
|
{
|
|
// see if there is a tokPlus, tokMinus or tokNot infront of the factor
|
|
TreeNode* node;
|
|
switch (currentToken.type)
|
|
{
|
|
case tokPlus:
|
|
matchToken(tokPlus);
|
|
return Factor();
|
|
break;
|
|
|
|
case tokMinus:
|
|
preservedToken = currentToken;
|
|
matchToken(tokMinus);
|
|
node = Factor();
|
|
if (node->getType() == constantNode)
|
|
{
|
|
// in case of just a constant (-3) situation
|
|
Value num = node->getValue();
|
|
num.setNumber( -num.Number() );
|
|
node->setValue(num);
|
|
return node;
|
|
}
|
|
else
|
|
{
|
|
// in case of a variable or other situation (-a)
|
|
TreeNode* minus = new TreeNode(preservedToken, minusNode);
|
|
minus->appendChild(node);
|
|
return minus;
|
|
}
|
|
break;
|
|
|
|
case tokNot:
|
|
preservedToken = currentToken;
|
|
matchToken(tokNot);
|
|
node = Factor();
|
|
{ // extra scope needed to localize not_Node
|
|
TreeNode* not_Node = new TreeNode(preservedToken, notNode);
|
|
not_Node->appendChild(node);
|
|
return not_Node;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// fall-through safety
|
|
return Factor();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
TreeNode* Parser::Term()
|
|
{
|
|
TreeNode* termNode = signedFactor();
|
|
TreeNode* pos = termNode;
|
|
TreeNode* left = NULL;
|
|
TreeNode* right = NULL;
|
|
|
|
while ( (currentToken.type == tokMul) || (currentToken.type == tokDev) || (currentToken.type == tokAnd) )
|
|
{
|
|
// while is is a multiplicative operator do...
|
|
left = pos;
|
|
pos = new TreeNode(currentToken, Unknown);
|
|
pos->appendChild(left);
|
|
|
|
switch (currentToken.type)
|
|
{
|
|
case tokMul:
|
|
matchToken(tokMul);
|
|
right = signedFactor();
|
|
pos->setType(mulNode);
|
|
break;
|
|
|
|
case tokDev:
|
|
matchToken(tokDev);
|
|
right = signedFactor();
|
|
pos->setType(divNode);
|
|
break;
|
|
|
|
case tokAnd:
|
|
matchToken(tokAnd);
|
|
right = signedFactor();
|
|
pos->setType(andNode);
|
|
break;
|
|
|
|
default:
|
|
Error(currentToken, i18n("Expected '*' or '/'"), 1030);
|
|
getToken();
|
|
return pos;
|
|
break;
|
|
}
|
|
if (right != NULL) pos->appendChild(right);
|
|
termNode = pos;
|
|
}
|
|
return termNode;
|
|
}
|
|
|
|
|
|
bool Parser::isAddOp(Token t)
|
|
{
|
|
return ( (t.type == tokPlus) ||
|
|
(t.type == tokMinus) ||
|
|
(t.type == tokGt) ||
|
|
(t.type == tokGe) ||
|
|
(t.type == tokLt) ||
|
|
(t.type == tokLe) ||
|
|
(t.type == tokEq) ||
|
|
(t.type == tokNe) ||
|
|
|
|
(t.type == tokOr) ||
|
|
(t.type == tokGe) );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------*/
|
|
/* Parse and Translate an Expression */
|
|
TreeNode* Parser::Expression()
|
|
{
|
|
TreeNode* retExp = Term(); // preset the base-TreeNode as it eventually will be returned
|
|
TreeNode* pos = retExp;
|
|
TreeNode* left = NULL;
|
|
TreeNode* right = NULL;
|
|
|
|
while ( isAddOp(currentToken) )
|
|
{
|
|
left = pos;
|
|
pos = new TreeNode(currentToken, Unknown);
|
|
pos->appendChild(left);
|
|
switch (currentToken.type)
|
|
{
|
|
case tokPlus:
|
|
matchToken(tokPlus);
|
|
right = Term();
|
|
pos->setType(addNode);
|
|
break;
|
|
|
|
case tokMinus:
|
|
matchToken(tokMinus);
|
|
right = Term();
|
|
pos->setType(subNode);
|
|
break;
|
|
|
|
case tokGt:
|
|
matchToken(tokGt);
|
|
right = Term();
|
|
pos->setType(nodeGT);
|
|
break;
|
|
|
|
case tokLt:
|
|
matchToken(tokLt);
|
|
right = Term();
|
|
pos->setType(nodeLT);
|
|
break;
|
|
|
|
case tokGe:
|
|
matchToken(tokGe);
|
|
right = Term();
|
|
pos->setType(nodeGE);
|
|
break;
|
|
|
|
case tokLe:
|
|
matchToken(tokLe);
|
|
right = Term();
|
|
pos->setType(nodeLE);
|
|
break;
|
|
|
|
case tokEq:
|
|
matchToken(tokEq);
|
|
right = Term();
|
|
pos->setType(nodeEQ);
|
|
break;
|
|
|
|
case tokNe:
|
|
matchToken(tokNe);
|
|
right = Term();
|
|
pos->setType(nodeNE);
|
|
break;
|
|
|
|
case tokOr:
|
|
matchToken(tokOr);
|
|
right = Term();
|
|
pos->setType(orNode);
|
|
break;
|
|
|
|
default:
|
|
Error(currentToken, i18n("Expected '*' or '/'"), 1040);
|
|
getToken();
|
|
return pos;
|
|
break;
|
|
}
|
|
if (right != NULL) pos->appendChild(right);
|
|
retExp = pos;
|
|
}
|
|
return retExp;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::Assignment(Token t)
|
|
{
|
|
TreeNode* node = new TreeNode(t, assignNode);
|
|
matchToken(tokAssign); // match the '='
|
|
|
|
// the child is the expression or RHV of assignment
|
|
TreeNode* expr = NULL;
|
|
// if (currentToken.type == tokUnknown) expr = Other(); // in case of an functioncall
|
|
// else expr = Expression(); -------> fuctioncalls get caught in Expression() and co.
|
|
|
|
expr = Expression();
|
|
|
|
node->appendChild(expr);
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::Statement()
|
|
{
|
|
kdDebug(0)<<"Parser::Statement()"<<endl;
|
|
while (currentToken.type == tokEOL) getToken(); // statements can allways start on newlines
|
|
switch (currentToken.type)
|
|
{
|
|
case tokLearn : return Learn(); break;
|
|
|
|
case tokIf : return If(); break;
|
|
case tokFor : return For(); break;
|
|
case tokForEach : return ForEach(); break;
|
|
case tokWhile : return While(); break;
|
|
case tokRun : return ExternalRun(); break;
|
|
case tokReturn : return Return(); break;
|
|
case tokBreak : return Break(); break;
|
|
case tokUnknown : return Other(); break; //assignment or function call
|
|
|
|
case tokClear : return Clear(); break;
|
|
case tokGo : return Go(); break;
|
|
case tokGoX : return GoX(); break;
|
|
case tokGoY : return GoY(); break;
|
|
case tokForward : return Forward(); break;
|
|
case tokBackward : return Backward(); break;
|
|
case tokDirection : return Direction(); break;
|
|
case tokTurnLeft : return TurnLeft(); break;
|
|
case tokTurnRight : return TurnRight(); break;
|
|
case tokCenter : return Center(); break;
|
|
case tokSetPenWidth : return SetPenWidth(); break;
|
|
case tokPenUp : return PenUp(); break;
|
|
case tokPenDown : return PenDown(); break;
|
|
case tokSetFgColor : return SetFgColor(); break;
|
|
case tokSetBgColor : return SetBgColor(); break;
|
|
case tokResizeCanvas : return ResizeCanvas(); break;
|
|
case tokSpriteShow : return SpriteShow(); break;
|
|
case tokSpriteHide : return SpriteHide(); break;
|
|
case tokSpritePress : return SpritePress(); break;
|
|
case tokSpriteChange : return SpriteChange(); break;
|
|
|
|
case tokPrint : return Print(); break;
|
|
case tokInputWindow : return InputWindow(); break;
|
|
case tokMessage : return Message(); break;
|
|
case tokFontType : return FontType(); break;
|
|
case tokFontSize : return FontSize(); break;
|
|
case tokRepeat : return Repeat(); break;
|
|
case tokRandom : return Random(); break;
|
|
case tokWait : return Wait(); break;
|
|
case tokWrapOn : return WrapOn(); break;
|
|
case tokWrapOff : return WrapOff(); break;
|
|
case tokReset : return Reset(); break;
|
|
|
|
case tokEOF : return EndOfFile(); break;
|
|
|
|
case tokEnd : Error(currentToken, i18n("Cannot understand ']'"), 1050);
|
|
getToken();
|
|
return new TreeNode(currentToken, Unknown);
|
|
break;
|
|
|
|
case tokBegin : Error(currentToken, i18n("Cannot understand '['"), 1050);
|
|
getToken();
|
|
return new TreeNode(currentToken, Unknown);
|
|
break;
|
|
|
|
default : break;
|
|
}
|
|
if (currentToken.type != tokEnd)
|
|
{
|
|
Error(currentToken, i18n("Cannot understand '%1'").arg(currentToken.look), 1060);
|
|
}
|
|
|
|
getToken();
|
|
return new TreeNode(currentToken, Unknown); // fall-though for unknowns
|
|
}
|
|
|
|
|
|
TreeNode* Parser::Block()
|
|
{
|
|
TreeNode* block = new TreeNode(currentToken, blockNode, "block");
|
|
|
|
while (currentToken.type == tokEOL) getToken(); // skip newlines
|
|
matchToken(tokBegin);
|
|
while (currentToken.type == tokEOL) getToken(); // skip newlines
|
|
while ( (currentToken.type != tokEnd) && (currentToken.type != tokEOF) )
|
|
{
|
|
block->appendChild( Statement() );
|
|
while (currentToken.type == tokEOL) getToken(); // blocks can have newlines between their statements
|
|
}
|
|
matchToken(tokEnd);
|
|
return block;
|
|
}
|
|
|
|
|
|
//
|
|
// Turtle Funktions
|
|
//
|
|
|
|
// Functions that take NO arguments
|
|
|
|
TreeNode* Parser::Clear()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, ClearNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::Center()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, CenterNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::PenUp()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, PenUpNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::PenDown()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, PenDownNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::SpriteShow()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, SpriteShowNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::SpriteHide()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, SpriteHideNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::SpritePress()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, SpritePressNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::WrapOn()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, WrapOnNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::WrapOff()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, WrapOffNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::Reset()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, ResetNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Functions that take 1 arguments
|
|
|
|
TreeNode* Parser::GoX()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, GoXNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::GoY()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, GoYNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::Forward()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, ForwardNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::Backward()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, BackwardNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::Direction()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, DirectionNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::TurnLeft()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, TurnLeftNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::TurnRight()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, TurnRightNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::SetPenWidth()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, SetPenWidthNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::Message()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, MessageNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::InputWindow()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, InputWindowNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
node->appendChild( Expression() );
|
|
// matchToken(tokEOL); this command can return values so can be used as expression/parameter
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::SpriteChange()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, SpriteChangeNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::FontType()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, FontTypeNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::FontSize()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, FontSizeNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::Wait()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, WaitNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::ExternalRun()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, runNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
node->appendChild( Expression() );
|
|
// matchToken(tokEOL); this command can return values so can be used as expression/parameter
|
|
return node;
|
|
}
|
|
|
|
|
|
|
|
// Functions that take 2 arguments
|
|
|
|
TreeNode* Parser::Go()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, GoNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::ResizeCanvas()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, ResizeCanvasNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::Random()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, RandomNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
// matchToken(tokEOL); this command can return values so can be used as expression/parameter
|
|
return node;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Functions that take 3 arguments
|
|
|
|
TreeNode* Parser::SetFgColor()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, SetFgColorNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::SetBgColor()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, SetBgColorNode);
|
|
preservedToken = currentToken;
|
|
getToken();
|
|
appendParameters(node);
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Weirdo's (learn, execution controllers, print, and Other()s)
|
|
|
|
TreeNode* Parser::Learn()
|
|
{
|
|
preservedToken = currentToken;
|
|
matchToken(tokLearn); // skip the 'dummy' command
|
|
TreeNode* func = new TreeNode(currentToken, functionNode);
|
|
getToken(); // get the token after the function's name
|
|
|
|
TreeNode* idList = new TreeNode(currentToken, idListNode, "idlist");
|
|
if (currentToken.type != tokBegin)
|
|
{
|
|
if (currentToken.type == tokUnknown) idList->appendChild( getId() );
|
|
else
|
|
{
|
|
Error(currentToken, "Expected a parameter name or a '[' after the learn command.", 3030);
|
|
getToken(); // this recovers from the error
|
|
}
|
|
|
|
while (currentToken.type == tokComma)
|
|
{
|
|
matchToken(tokComma);
|
|
idList->appendChild( getId() );
|
|
}
|
|
}
|
|
func->appendChild(idList);
|
|
|
|
learnedFunctionList.append( func->getLook() ); // publish the function
|
|
|
|
func->appendChild( Block() );
|
|
|
|
return func;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::If()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, ifNode);
|
|
preservedToken = currentToken;
|
|
matchToken(tokIf);
|
|
|
|
node->appendChild( Expression() );
|
|
|
|
if (currentToken.type == tokDo) getToken(); // skip dummy word 'do'
|
|
|
|
if (currentToken.type == tokBegin) node->appendChild( Block() ); // if followed by a block
|
|
else node->appendChild( Statement() ); // if followed by single statement
|
|
|
|
while (currentToken.type == tokEOL) getToken(); // allow the else keyword to be on later lines
|
|
|
|
if (currentToken.type == tokElse) // else part
|
|
{
|
|
matchToken(tokElse);
|
|
while (currentToken.type == tokEOL) getToken();
|
|
if (currentToken.type == tokDo) getToken(); // next word
|
|
|
|
if(currentToken.type == tokBegin) node->appendChild( Block() ); // else is followed by block
|
|
else node->appendChild( Statement() );
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::While()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, whileNode);
|
|
preservedToken = currentToken;
|
|
matchToken(tokWhile);
|
|
node->appendChild( Expression() );
|
|
node->appendChild( Block() );
|
|
return node;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::For()
|
|
{
|
|
TreeNode* fNode = new TreeNode(currentToken, forNode);
|
|
preservedToken = currentToken;
|
|
matchToken(tokFor);
|
|
fNode->appendChild( getId() ); // loop id
|
|
matchToken(tokAssign);
|
|
|
|
fNode->appendChild( Expression() ); // start value expression
|
|
matchToken(tokTo);
|
|
fNode->appendChild( Expression() ); // stop value expression
|
|
|
|
if (currentToken.type == tokStep)
|
|
{
|
|
matchToken(tokStep);
|
|
fNode->appendChild( Expression() ); //step expression
|
|
}
|
|
|
|
while (currentToken.type == tokEOL) getToken(); // newlines are allowed
|
|
if (currentToken.type == tokBegin) fNode->appendChild( Block() ); // for followed by a block
|
|
else fNode->appendChild( Statement() ); // while followed by single statement
|
|
|
|
return fNode;
|
|
}
|
|
|
|
|
|
TreeNode* Parser::Repeat()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, RepeatNode);
|
|
preservedToken = currentToken; // preserve token, else Match() will make sure it gets lost
|
|
matchToken(tokRepeat);
|
|
node->appendChild( Expression() );
|
|
node->appendChild( Block() );
|
|
return node;
|
|
}
|
|
|
|
|
|
|
|
TreeNode* Parser::ForEach()
|
|
{
|
|
TreeNode* fNode = new TreeNode(currentToken, forEachNode);
|
|
preservedToken = currentToken;
|
|
matchToken(tokForEach);
|
|
|
|
fNode->appendChild( Expression() );
|
|
matchToken(tokIn);
|
|
fNode->appendChild( Expression() );
|
|
|
|
if (currentToken.type == tokBegin) fNode->appendChild( Block() ); // for followed by a block
|
|
else fNode->appendChild( Statement() ); // while followed by single statement
|
|
|
|
return fNode;
|
|
}
|
|
|
|
TreeNode* Parser::Print()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, printNode);
|
|
preservedToken = currentToken; // preserve token, else Match() will make sure it gets lost
|
|
getToken();
|
|
if (currentToken.type == tokEOL) return node; // print called without expressions
|
|
node->appendChild( Expression() ); // first expression
|
|
// following strings or expressions
|
|
while (currentToken.type == tokComma)
|
|
{
|
|
getToken(); // the comma
|
|
node->appendChild( Expression() );
|
|
}
|
|
matchToken(tokEOL);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::Return()
|
|
{
|
|
TreeNode* ret = new TreeNode(currentToken, returnNode);
|
|
matchToken(tokReturn);
|
|
|
|
ret->appendChild( Expression() );
|
|
|
|
return ret;
|
|
}
|
|
|
|
TreeNode* Parser::Break()
|
|
{
|
|
TreeNode* brk = new TreeNode(currentToken, breakNode);
|
|
matchToken(tokBreak);
|
|
|
|
return brk;
|
|
}
|
|
|
|
TreeNode* Parser::EndOfFile()
|
|
{
|
|
TreeNode* node = new TreeNode(currentToken, EndOfFileNode);
|
|
return node;
|
|
}
|
|
|
|
TreeNode* Parser::Other()
|
|
{
|
|
// this is either an assignment or a function call!
|
|
kdDebug(0)<<"Parser::Other()"<<endl;
|
|
Token rememberedToken = currentToken; // preserve token, else Match() will make sure it gets lost
|
|
matchToken(tokUnknown);
|
|
|
|
if (currentToken.type == tokAssign) return Assignment(rememberedToken);
|
|
else if (learnedFunctionList.contains(rememberedToken.look) > 0)
|
|
{
|
|
TreeNode* node;
|
|
node = FunctionCall(rememberedToken);
|
|
// node->setType(funcReturnNode);
|
|
return node;
|
|
}
|
|
|
|
Error(rememberedToken, i18n("'%1' is neither a Logo command nor a learned command.").arg(rememberedToken.look), 1020);
|
|
TreeNode* errNode = new TreeNode(rememberedToken, Unknown);
|
|
|
|
// skip the rest of the line
|
|
while (currentToken.type != tokEOL) getToken();
|
|
|
|
return errNode;
|
|
}
|
|
|
|
|
|
void Parser::Error(Token& t, const TQString& s, uint code)
|
|
{
|
|
emit ErrorMsg(t, s, code);
|
|
}
|
|
|
|
|
|
#include "parser.moc"
|