1503 lines
30 KiB
C++
1503 lines
30 KiB
C++
/*******************************************************************************
|
|
* fnsyntax.cpp
|
|
*
|
|
* This module implements the the function type used by iso surfaces and
|
|
* the function pattern.
|
|
*
|
|
* This module is inspired by code by D. Skarda, T. Bily and R. Suzuki.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray 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 Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/source/backend/parser/fnsyntax.cpp $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
#include <limits.h>
|
|
|
|
// frame.h must always be the first POV file included (pulls in platform config)
|
|
#include "backend/frame.h"
|
|
#include "backend/parser/parse.h"
|
|
#include "backend/vm/fnpovfpu.h"
|
|
#include "backend/math/mathutil.h"
|
|
|
|
// this must be the last file included
|
|
#include "base/povdebug.h"
|
|
|
|
namespace pov
|
|
{
|
|
|
|
/*****************************************************************************
|
|
* Local typedefs
|
|
******************************************************************************/
|
|
|
|
struct ExprParserTableEntry
|
|
{
|
|
int stage;
|
|
TOKEN token;
|
|
bool (Parser::*operation)(Parser::ExprNode *&, int, int);
|
|
int next;
|
|
int op;
|
|
};
|
|
|
|
struct ExprParserErrorEntry
|
|
{
|
|
int stage;
|
|
const char *expected;
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
* Local variables
|
|
******************************************************************************/
|
|
|
|
const ExprParserErrorEntry expr_parser_error_table[] =
|
|
{
|
|
{ 35, "operator" },
|
|
{ 45, "." },
|
|
{ 40, "sign or operand" },
|
|
{ 50, "operand" },
|
|
{ 55, ")" },
|
|
{ 60, "color or vector member" },
|
|
{ -1, NULL }
|
|
};
|
|
|
|
const ExprParserTableEntry expr_parser_table[] =
|
|
{
|
|
// logical or
|
|
{ 5, BAR_TOKEN, &Parser::expr_grow, 40, OP_OR }, // 0
|
|
// logical and
|
|
{ 10, AMPERSAND_TOKEN, &Parser::expr_grow, 40, OP_AND }, // 1
|
|
// equal, not equal
|
|
{ 15, EQUALS_TOKEN, &Parser::expr_grow, 40, OP_CMP_EQ }, // 2
|
|
{ 15, REL_NE_TOKEN, &Parser::expr_grow, 40, OP_CMP_NE }, // 3
|
|
// smaller and/or equal, greater and/or equal
|
|
{ 15, LEFT_ANGLE_TOKEN, &Parser::expr_grow, 40, OP_CMP_LT }, // 4
|
|
{ 15, REL_LE_TOKEN, &Parser::expr_grow, 40, OP_CMP_LE }, // 5
|
|
{ 15, RIGHT_ANGLE_TOKEN, &Parser::expr_grow, 40, OP_CMP_GT }, // 6
|
|
{ 15, REL_GE_TOKEN, &Parser::expr_grow, 40, OP_CMP_GE }, // 7
|
|
// plus, minus
|
|
{ 20, PLUS_TOKEN, &Parser::expr_grow, 40, OP_ADD }, // 8
|
|
{ 20, DASH_TOKEN, &Parser::expr_grow, 40, OP_SUB }, // 9
|
|
// multiply, divide
|
|
{ 25, STAR_TOKEN, &Parser::expr_grow, 40, OP_MUL }, // 10
|
|
{ 25, SLASH_TOKEN, &Parser::expr_grow, 40, OP_DIV }, // 11
|
|
// ')', '}' or ',' - end of function
|
|
{ 35, RIGHT_PAREN_TOKEN, &Parser::expr_ret, -1, OP_NONE }, // 12
|
|
{ 35, RIGHT_CURLY_TOKEN, &Parser::expr_ret, -1, OP_NONE }, // 13
|
|
{ 35, COMMA_TOKEN, &Parser::expr_ret, -1, OP_NONE }, // 14
|
|
{ 35, LAST_TOKEN, &Parser::expr_err, -1, OP_NONE }, // 15
|
|
// vector/color member access
|
|
{ 45, PERIOD_TOKEN, &Parser::expr_grow, 60, OP_DOT }, // 16
|
|
{ 45, LAST_TOKEN, &Parser::expr_err, -1, OP_NONE }, // 17
|
|
// unary plus, unary minus, (logical not - disabled)
|
|
{ 40, PLUS_TOKEN, &Parser::expr_noop, 50, OP_NONE }, // 18
|
|
{ 40, DASH_TOKEN, &Parser::expr_grow, 50, OP_NEG }, // 19
|
|
{ 40, EXCLAMATION_TOKEN, &Parser::expr_err, -1, OP_NOT }, // 20
|
|
// constant, variable, (expression), function
|
|
{ 50, FLOAT_TOKEN, &Parser::expr_put, 5, OP_CONSTANT }, // 21
|
|
{ 50, FLOAT_ID_TOKEN, &Parser::expr_put, 5, OP_VARIABLE }, // 22
|
|
{ 50, FUNCT_ID_TOKEN, &Parser::expr_call, 5, OP_CALL }, // 23
|
|
{ 50, VECTFUNCT_ID_TOKEN,&Parser::expr_call, 45, OP_CALL }, // 24
|
|
{ 50, LEFT_PAREN_TOKEN, &Parser::expr_new, 55, OP_FIRST }, // 25
|
|
{ 50, LAST_TOKEN, &Parser::expr_err, -1, OP_NONE }, // 26
|
|
// (expression)
|
|
{ 55, RIGHT_PAREN_TOKEN, &Parser::expr_noop, 5, OP_NONE }, // 27
|
|
{ 55, LAST_TOKEN, &Parser::expr_err, -1, OP_NONE }, // 28
|
|
// vector/color members
|
|
{ 60, FLOAT_ID_TOKEN, &Parser::expr_put, 5, OP_MEMBER }, // 29
|
|
{ 60, T_TOKEN, &Parser::expr_put, 5, OP_MEMBER }, // 30
|
|
{ 60, RED_TOKEN, &Parser::expr_put, 5, OP_MEMBER }, // 31
|
|
{ 60, GREEN_TOKEN, &Parser::expr_put, 5, OP_MEMBER }, // 32
|
|
{ 60, BLUE_TOKEN, &Parser::expr_put, 5, OP_MEMBER }, // 33
|
|
{ 60, FILTER_TOKEN, &Parser::expr_put, 5, OP_MEMBER }, // 34
|
|
{ 60, TRANSMIT_TOKEN, &Parser::expr_put, 5, OP_MEMBER }, // 35
|
|
{ 60, GRAY_TOKEN, &Parser::expr_put, 5, OP_MEMBER }, // 36
|
|
{ 60, LAST_TOKEN, &Parser::expr_err, -1, OP_NONE } // 37
|
|
};
|
|
|
|
// parse_expr has to start with first unary operator [trf]
|
|
const int START_LEFTMOST_PARSE_INDEX = 18;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* FNSyntax_ParseExpression
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* ExprNode - parsed expression root pointer
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Parse and optimise an expression.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
Parser::ExprNode *Parser::FNSyntax_ParseExpression()
|
|
{
|
|
ExprNode *expression = NULL;
|
|
|
|
expression = parse_expr();
|
|
optimise_expr(expression);
|
|
|
|
return expression;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* FNSyntax_GetTrapExpression
|
|
*
|
|
* INPUT
|
|
*
|
|
* trap - number of the trap
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* ExprNode - expression root pointer
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Generate an expression which only contains a trap.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
Parser::ExprNode *Parser::FNSyntax_GetTrapExpression(unsigned int trap)
|
|
{
|
|
ExprNode *expression = NULL;
|
|
|
|
expression = new_expr_node(0, OP_TRAP);
|
|
expression->trap = trap;
|
|
|
|
return expression;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* FNSyntax_DeleteExpression
|
|
*
|
|
* INPUT
|
|
*
|
|
* node - root node of the (sub-) tree to delete
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Delete an expression (sub-) tree.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::FNSyntax_DeleteExpression(ExprNode *node)
|
|
{
|
|
ExprNode *temp = NULL;
|
|
|
|
for(ExprNode *i = node; i != NULL; i = i->next)
|
|
{
|
|
if(temp != NULL)
|
|
{
|
|
POV_FREE(temp);
|
|
}
|
|
|
|
FNSyntax_DeleteExpression(i->child);
|
|
|
|
if((i->op == OP_VARIABLE) || (i->op == OP_MEMBER))
|
|
{
|
|
POV_FREE(i->variable);
|
|
}
|
|
else if(i->op == OP_CALL)
|
|
{
|
|
if((i->call.token == FUNCT_ID_TOKEN) || (i->call.token == VECTFUNCT_ID_TOKEN))
|
|
sceneData->functionVM->RemoveFunction(i->call.fn);
|
|
POV_FREE(i->call.name);
|
|
}
|
|
|
|
temp = i;
|
|
}
|
|
|
|
if(temp != NULL)
|
|
{
|
|
POV_FREE(temp);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* parse_expr
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* ExprNode - parsed expression root pointer
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Parse an expression or subexpression.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
Parser::ExprNode *Parser::parse_expr()
|
|
{
|
|
ExprNode *current = NULL;
|
|
ExprNode *node = NULL;
|
|
TOKEN token;
|
|
int start_index;
|
|
int i;
|
|
|
|
current = node = new_expr_node(0, OP_FIRST);
|
|
|
|
start_index = START_LEFTMOST_PARSE_INDEX;
|
|
token = expr_get_token();
|
|
|
|
while(true)
|
|
{
|
|
// search for matching token
|
|
for(i = start_index; ; i++)
|
|
{
|
|
if((expr_parser_table[i].token == token) ||
|
|
(expr_parser_table[i].token == LAST_TOKEN))
|
|
break;
|
|
}
|
|
|
|
// execute operation
|
|
if((this->*(expr_parser_table[i].operation))(current, expr_parser_table[i].stage, expr_parser_table[i].op) == false)
|
|
break;
|
|
|
|
// find next index start
|
|
if(expr_parser_table[i].next >= 0)
|
|
{
|
|
if(expr_parser_table[i].next < expr_parser_table[i].stage)
|
|
start_index = 0;
|
|
// searching the whole table allows forward references
|
|
// to stages with a lower stage number [trf]
|
|
while(expr_parser_table[start_index].stage != expr_parser_table[i].next)
|
|
start_index++;
|
|
}
|
|
|
|
token = expr_get_token();
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* expr_get_token
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* TOKEN - simplified token from Get_Token
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Gets a token and simplifies it for use by the expression parser.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
TOKEN Parser::expr_get_token()
|
|
{
|
|
Get_Token();
|
|
|
|
if(Token.Function_Id == X_TOKEN)
|
|
return FLOAT_ID_TOKEN;
|
|
else if(Token.Function_Id == Y_TOKEN)
|
|
return FLOAT_ID_TOKEN;
|
|
else if(Token.Function_Id == Z_TOKEN)
|
|
return FLOAT_ID_TOKEN;
|
|
else if(Token.Function_Id == U_TOKEN)
|
|
return FLOAT_ID_TOKEN;
|
|
else if(Token.Function_Id == V_TOKEN)
|
|
return FLOAT_ID_TOKEN;
|
|
else if(Token.Function_Id == IDENTIFIER_TOKEN)
|
|
return FLOAT_ID_TOKEN;
|
|
else if(Token.Function_Id == CLOCK_TOKEN)
|
|
{
|
|
Token.Token_Float = clockValue;
|
|
return FLOAT_TOKEN;
|
|
}
|
|
else if(Token.Function_Id == PI_TOKEN)
|
|
{
|
|
Token.Token_Float = M_PI;
|
|
return FLOAT_TOKEN;
|
|
}
|
|
else if(Token.Function_Id == RED_TOKEN)
|
|
return RED_TOKEN;
|
|
else if(Token.Function_Id == GREEN_TOKEN)
|
|
return GREEN_TOKEN;
|
|
else if(Token.Function_Id == BLUE_TOKEN)
|
|
return BLUE_TOKEN;
|
|
else if(Token.Function_Id == FILTER_TOKEN)
|
|
return FILTER_TOKEN;
|
|
else if(Token.Function_Id == TRANSMIT_TOKEN)
|
|
return TRANSMIT_TOKEN;
|
|
else if(Token.Function_Id == T_TOKEN)
|
|
return T_TOKEN;
|
|
else if(Token.Function_Id == GRAY_TOKEN)
|
|
return GRAY_TOKEN;
|
|
|
|
if(Token.Token_Id == FLOAT_FUNCT_TOKEN)
|
|
{
|
|
if(Token.Function_Id == FLOAT_TOKEN)
|
|
return FLOAT_TOKEN;
|
|
else if(Token.Function_Id == FLOAT_ID_TOKEN)
|
|
{
|
|
Token.Token_Float = *((DBL *)Token.Data);
|
|
return FLOAT_TOKEN;
|
|
}
|
|
|
|
return FUNCT_ID_TOKEN;
|
|
}
|
|
|
|
return Token.Token_Id;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* new_expr_node
|
|
*
|
|
* INPUT
|
|
*
|
|
* stage - stage/precedence of new node
|
|
* op - operation of new node
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* ExprNode - new expression node structure
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Creates a new expression node structure.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
Parser::ExprNode *Parser::new_expr_node(int stage, int op)
|
|
{
|
|
ExprNode *node = NULL;
|
|
|
|
node = (ExprNode *)POV_MALLOC(sizeof(ExprNode), "ExprNode");
|
|
node->parent = NULL;
|
|
node->child = NULL;
|
|
node->prev = NULL;
|
|
node->next = NULL;
|
|
node->stage = stage;
|
|
node->op = op;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* expr_noop
|
|
*
|
|
* INPUT
|
|
*
|
|
* current - current poistion in expression tree
|
|
* stage - stage/precedence of operation
|
|
* op - operation
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* bool - continue to parse expression?
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Expression parser manipulation function that does nothing.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::expr_noop(ExprNode *&, int, int)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* expr_grow
|
|
*
|
|
* INPUT
|
|
*
|
|
* current - current poistion in expression tree
|
|
* stage - stage/precedence of operation
|
|
* op - operation
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* bool - continue to parse expression?
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Expression parser manipulation function that grows the tree in the
|
|
* correct place based on the stage/level of the other nodes in the tree.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::expr_grow(ExprNode *¤t, int stage, int op)
|
|
{
|
|
ExprNode *node = NULL;
|
|
|
|
if(current == NULL)
|
|
return false;
|
|
|
|
// the idea is this order: current, node, current->child
|
|
if(current->stage < stage)
|
|
{
|
|
while(current->child != NULL)
|
|
{
|
|
if(current->child->stage > stage)
|
|
break;
|
|
|
|
current = current->child;
|
|
|
|
if(current->stage == stage)
|
|
break;
|
|
}
|
|
}
|
|
else if(current->stage > stage)
|
|
{
|
|
while(current->parent != NULL)
|
|
{
|
|
current = current->parent;
|
|
|
|
if(current->stage <= stage)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(current->stage == stage)
|
|
{
|
|
while(current->next != NULL)
|
|
current = current->next;
|
|
|
|
node = new_expr_node(stage, op);
|
|
|
|
node->parent = current->parent;
|
|
node->prev = current;
|
|
current->next = node;
|
|
|
|
current = node;
|
|
}
|
|
else
|
|
{
|
|
node = new_expr_node(stage, OP_LEFTMOST);
|
|
|
|
node->parent = current;
|
|
node->child = current->child;
|
|
current->child = node;
|
|
for(ExprNode *ptr = node->child; ptr != NULL; ptr = ptr->next)
|
|
ptr->parent = node;
|
|
|
|
current = new_expr_node(stage, op);
|
|
current->prev = node;
|
|
node->next = current;
|
|
current->parent = node->parent;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* expr_call
|
|
*
|
|
* INPUT
|
|
*
|
|
* current - current poistion in expression tree
|
|
* stage - stage/precedence of operation
|
|
* op - operation
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* bool - continue to parse expression?
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Expression parser manipulation function that handles a function call.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::expr_call(ExprNode *¤t, int stage, int op)
|
|
{
|
|
ExprNode *node = NULL;
|
|
|
|
if(current == NULL)
|
|
return false;
|
|
|
|
node = new_expr_node(stage, op);
|
|
|
|
if(Token.Data != NULL)
|
|
{
|
|
node->call.fn = *((FUNCTION_PTR)Token.Data);
|
|
(void)sceneData->functionVM->GetFunctionAndReference(node->call.fn);
|
|
}
|
|
else
|
|
node->call.fn = 0;
|
|
node->call.token = Token.Function_Id;
|
|
node->call.name = POV_STRDUP(Token.Token_String);
|
|
while(current->child != NULL)
|
|
current = current->child;
|
|
|
|
current->child = node;
|
|
node->parent = current;
|
|
current = node;
|
|
|
|
if(expr_get_token() != LEFT_PAREN_TOKEN)
|
|
Expectation_Error("(");
|
|
|
|
current->child = node = parse_expr();
|
|
while(expr_get_token() == COMMA_TOKEN)
|
|
{
|
|
node->next = parse_expr();
|
|
node->next->parent = node->parent;
|
|
node = node->next;
|
|
}
|
|
|
|
if(Token.Token_Id != RIGHT_PAREN_TOKEN)
|
|
Expectation_Error(")");
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* expr_put
|
|
*
|
|
* INPUT
|
|
*
|
|
* current - current poistion in expression tree
|
|
* stage - stage/precedence of operation
|
|
* op - operation
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* bool - continue to parse expression?
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Expression parser manipulation function that adds a new node in the
|
|
* current location in the tree.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::expr_put(ExprNode *¤t, int stage, int op)
|
|
{
|
|
ExprNode *node = NULL;
|
|
|
|
if(current == NULL)
|
|
return false;
|
|
|
|
if(current->child != NULL)
|
|
return false;
|
|
|
|
node = new_expr_node(stage, op);
|
|
|
|
if(op == OP_CONSTANT)
|
|
{
|
|
node->number = Token.Token_Float;
|
|
}
|
|
else
|
|
{
|
|
node->variable = POV_STRDUP(Token.Token_String);
|
|
}
|
|
|
|
current->child = node;
|
|
node->parent = current;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* expr_new
|
|
*
|
|
* INPUT
|
|
*
|
|
* current - current poistion in expression tree
|
|
* stage - stage/precedence of operation
|
|
* op - operation
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* bool - continue to parse expression?
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Expression parser manipulation function that creates a new expression
|
|
* or subexpression.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::expr_new(ExprNode *¤t, int /*stage*/, int /*op*/)
|
|
{
|
|
ExprNode *node = NULL;
|
|
|
|
node = parse_expr();
|
|
if(node == NULL)
|
|
return false;
|
|
|
|
current->child = node;
|
|
node->parent = current;
|
|
node->stage = 10000; // needs to be higher than any other stage for expr_grow to work [trf]
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* expr_ret
|
|
*
|
|
* INPUT
|
|
*
|
|
* current - current poistion in expression tree
|
|
* stage - stage/precedence of operation
|
|
* op - operation
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* bool - continue to parse expression?
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Expression parser manipulation function that marks the end of parsing
|
|
* a expression or subexpression.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::expr_ret(ExprNode *&, int, int)
|
|
{
|
|
Unget_Token();
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* expr_err
|
|
*
|
|
* INPUT
|
|
*
|
|
* current - current poistion in expression tree
|
|
* stage - stage/precedence of operation
|
|
* op - operation
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* bool - continue to parse expression?
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Expression parser manipulation function that terminates all parsing
|
|
* and outputs a parse error message.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::expr_err(ExprNode *&, int stage, int)
|
|
{
|
|
int i;
|
|
|
|
if(stage == 35)
|
|
PossibleError("Suspicious identifier found in function!\n"
|
|
"If you want to call a function make sure the function you call has been declared.\n"
|
|
"If you call an internal function, make sure you have included 'functions.inc'.");
|
|
|
|
for(i = 0; (expr_parser_error_table[i].stage >= 0) && (expr_parser_error_table[i].expected != NULL); i++)
|
|
{
|
|
if(expr_parser_error_table[i].stage == stage)
|
|
Expectation_Error(expr_parser_error_table[i].expected);
|
|
}
|
|
|
|
Expectation_Error("valid function expression");
|
|
|
|
// unreachable, Expectation_Error stops parsing
|
|
return false;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* optimise_expr
|
|
*
|
|
* INPUT
|
|
*
|
|
* node - root node of the expression (sub-) tree to optimise
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Optimised an expression (sub-) tree. Currently it only combines
|
|
* constants next to each other. Other optimisations are still waiting
|
|
* to be implemented.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::optimise_expr(ExprNode *node)
|
|
{
|
|
ExprNode *left,*right,*ptr,*temp;
|
|
DBL result;
|
|
bool have_result;
|
|
int op,cnt;
|
|
|
|
if(node == NULL)
|
|
return;
|
|
|
|
if(node->op == OP_CALL)
|
|
{
|
|
if(node->call.token == POW_TOKEN)
|
|
{
|
|
node->op = OP_FIRST;
|
|
POV_FREE(node->call.name);
|
|
if(node->child != NULL)
|
|
{
|
|
node->child->op = OP_LEFTMOST;
|
|
if(node->child->next != NULL)
|
|
{
|
|
node->child->next->op = OP_POW;
|
|
node->child->next->prev = node->child;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(node->op < OP_FIRST) // using switch statement might be better [trf]
|
|
{
|
|
ptr = node->next;
|
|
if(ptr != NULL)
|
|
{
|
|
if(ptr->op == OP_NEG)
|
|
{
|
|
op = ptr->op;
|
|
cnt = 0;
|
|
for(ptr = node->next; ptr != NULL; ptr = ptr->next)
|
|
{
|
|
cnt++;
|
|
if(ptr->child != NULL)
|
|
break;
|
|
}
|
|
|
|
if(ptr != NULL)
|
|
{
|
|
optimise_expr(ptr->child);
|
|
if(ptr->child != NULL)
|
|
{
|
|
left = ptr->child;
|
|
if(left->op == OP_CONSTANT)
|
|
{
|
|
ptr->child = NULL;
|
|
|
|
if(node->next != NULL)
|
|
FNSyntax_DeleteExpression(node->next);
|
|
|
|
if(op == OP_NEG)
|
|
{
|
|
if((cnt % 2) == 0)
|
|
node->number = (left->number);
|
|
else
|
|
node->number = -(left->number);
|
|
}
|
|
POV_FREE(left);
|
|
node->op = OP_CONSTANT;
|
|
node->child = NULL;
|
|
node->prev = NULL;
|
|
node->next = NULL;
|
|
return; // early exit
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
optimise_expr(node->child);
|
|
for(ptr = node->next; ptr != NULL; ptr = ptr->next)
|
|
{
|
|
left = ptr->prev->child;
|
|
right = ptr->child;
|
|
|
|
if((right != NULL) && (ptr->op == OP_SUB))
|
|
{
|
|
if(right->op == OP_CONSTANT)
|
|
{
|
|
ptr->op = OP_ADD;
|
|
right->number = -right->number;
|
|
}
|
|
}
|
|
|
|
optimise_expr(right);
|
|
|
|
if((left != NULL) && (right != NULL) &&
|
|
(((ptr->op != OP_MUL) && (ptr->op != OP_DIV)) ||
|
|
!left_subtree_has_variable_expr(ptr)))
|
|
{
|
|
if((left->op == OP_CONSTANT) && (right->op == OP_CONSTANT))
|
|
{
|
|
have_result = true;
|
|
|
|
switch(ptr->op)
|
|
{
|
|
case OP_CMP_EQ:
|
|
result = (left->number == right->number);
|
|
break;
|
|
case OP_CMP_NE:
|
|
result = (left->number != right->number);
|
|
break;
|
|
case OP_CMP_LT:
|
|
result = (left->number < right->number);
|
|
break;
|
|
case OP_CMP_LE:
|
|
result = (left->number <= right->number);
|
|
break;
|
|
case OP_CMP_GT:
|
|
result = (left->number > right->number);
|
|
break;
|
|
case OP_CMP_GE:
|
|
result = (left->number >= right->number);
|
|
break;
|
|
case OP_ADD:
|
|
result = (left->number + right->number);
|
|
break;
|
|
case OP_SUB:
|
|
result = (left->number - right->number);
|
|
break;
|
|
case OP_OR:
|
|
result = (DBL)(((DBL)((((DBL)(left->number != 0)) + ((DBL)(right->number != 0))))) != 0); // match VM code
|
|
break;
|
|
case OP_MUL:
|
|
result = (left->number * right->number);
|
|
break;
|
|
case OP_DIV:
|
|
result = (left->number / right->number);
|
|
break;
|
|
case OP_AND:
|
|
result = (DBL)((((DBL)(left->number != 0)) * ((DBL)(right->number != 0)))); // match VM code
|
|
break;
|
|
case OP_POW:
|
|
result = pow(left->number, right->number);
|
|
break;
|
|
default:
|
|
have_result = false;
|
|
break;
|
|
}
|
|
|
|
if(have_result == true)
|
|
{
|
|
temp = ptr;
|
|
ptr->prev->next = ptr->next;
|
|
if(ptr->next != NULL)
|
|
ptr->next->prev = ptr->prev;
|
|
ptr = ptr->prev;
|
|
POV_FREE(temp->child);
|
|
POV_FREE(temp);
|
|
left->number = result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((node->next == NULL) && (node->child != NULL) && (node->op < OP_FIRST))
|
|
{
|
|
if((node->child->op == OP_CONSTANT) && (node->child->next == NULL))
|
|
{
|
|
node->number = left->number;
|
|
node->op = OP_CONSTANT;
|
|
POV_FREE(node->child);
|
|
node->child = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
optimise_expr(node->child);
|
|
|
|
optimise_call(node);
|
|
|
|
if((node->child != NULL) && (node->op < OP_FIRST))
|
|
{
|
|
if((node->child->op == OP_CONSTANT) && (node->child->next == NULL))
|
|
{
|
|
node->number = node->child->number;
|
|
POV_FREE(node->child);
|
|
node->child = NULL;
|
|
node->op = OP_CONSTANT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* optimise_call
|
|
*
|
|
* INPUT
|
|
*
|
|
* node - node of a function call
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Optimises a function call if it has only constant arguments
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::optimise_call(ExprNode *node)
|
|
{
|
|
DBL result = 0.0;
|
|
bool have_result = true;;
|
|
|
|
if(node->op != OP_CALL)
|
|
return;
|
|
if(node->child == NULL)
|
|
return;
|
|
if(node->child->op != OP_CONSTANT)
|
|
return;
|
|
|
|
switch(node->call.token)
|
|
{
|
|
case SIN_TOKEN:
|
|
result = sin(node->child->number);
|
|
break;
|
|
case COS_TOKEN:
|
|
result = cos(node->child->number);
|
|
break;
|
|
case TAN_TOKEN:
|
|
result = tan(node->child->number);
|
|
break;
|
|
case ASIN_TOKEN:
|
|
result = asin(node->child->number);
|
|
break;
|
|
case ACOS_TOKEN:
|
|
result = acos(node->child->number);
|
|
break;
|
|
case ATAN_TOKEN:
|
|
result = atan(node->child->number);
|
|
break;
|
|
case SINH_TOKEN:
|
|
result = sinh(node->child->number);
|
|
break;
|
|
case COSH_TOKEN:
|
|
result = cosh(node->child->number);
|
|
break;
|
|
case TANH_TOKEN:
|
|
result = tanh(node->child->number);
|
|
break;
|
|
case ASINH_TOKEN:
|
|
result = asinh(node->child->number);
|
|
break;
|
|
case ACOSH_TOKEN:
|
|
result = acosh(node->child->number);
|
|
break;
|
|
case ATANH_TOKEN:
|
|
result = atanh(node->child->number);
|
|
break;
|
|
case ABS_TOKEN:
|
|
result = fabs(node->child->number);
|
|
break;
|
|
case RADIANS_TOKEN:
|
|
result = node->child->number * M_PI / 180.0;
|
|
break;
|
|
case DEGREES_TOKEN:
|
|
result = node->child->number * 180.0 / M_PI;
|
|
break;
|
|
case FLOOR_TOKEN:
|
|
result = floor(node->child->number);
|
|
break;
|
|
case INT_TOKEN:
|
|
result = (int)(node->child->number);
|
|
break;
|
|
case CEIL_TOKEN:
|
|
result = ceil(node->child->number);
|
|
break;
|
|
case SQRT_TOKEN:
|
|
result = sqrt(node->child->number);
|
|
break;
|
|
case EXP_TOKEN:
|
|
result = exp(node->child->number);
|
|
break;
|
|
case LN_TOKEN:
|
|
if(node->child->number > 0.0)
|
|
result = log(node->child->number);
|
|
else
|
|
Error("Domain error in 'ln'.");
|
|
break;
|
|
case LOG_TOKEN:
|
|
if(node->child->number > 0.0)
|
|
result = log10(node->child->number);
|
|
else
|
|
Error("Domain error in 'log'.");
|
|
break;
|
|
case MIN_TOKEN:
|
|
have_result = false;
|
|
break;
|
|
case MAX_TOKEN:
|
|
have_result = false;
|
|
break;
|
|
case ATAN2_TOKEN:
|
|
have_result = false;
|
|
break;
|
|
case POW_TOKEN:
|
|
have_result = false;
|
|
break;
|
|
case MOD_TOKEN:
|
|
have_result = false;
|
|
break;
|
|
case SELECT_TOKEN:
|
|
have_result = false;
|
|
break;
|
|
case FUNCT_ID_TOKEN:
|
|
have_result = false;
|
|
break;
|
|
case VECTFUNCT_ID_TOKEN:
|
|
have_result = false;
|
|
break;
|
|
default:
|
|
have_result = false;
|
|
break;
|
|
}
|
|
|
|
if(have_result == true)
|
|
{
|
|
POV_FREE(node->call.name);
|
|
node->number = result;
|
|
node->op = OP_CONSTANT;
|
|
POV_FREE(node->child);
|
|
node->child = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* right_subtree_has_variable_expr
|
|
*
|
|
* INPUT
|
|
*
|
|
* node - root node of the (sub-) tree to search for variables
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Searches an expression tree to determine if it contains any variables
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::right_subtree_has_variable_expr(ExprNode *node)
|
|
{
|
|
if(node == NULL)
|
|
return false;
|
|
|
|
for(ExprNode *i = node; i != NULL; i = i->next)
|
|
{
|
|
if(i->op == OP_VARIABLE)
|
|
return true;
|
|
|
|
if(i->child != NULL)
|
|
{
|
|
if(right_subtree_has_variable_expr(i->child) == true)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* left_subtree_has_variable_expr
|
|
*
|
|
* INPUT
|
|
*
|
|
* node - root node of the (sub-) tree to search for variables
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Searches an expression tree to determine if it contains any variables
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::left_subtree_has_variable_expr(ExprNode *node)
|
|
{
|
|
if(node == NULL)
|
|
return false;
|
|
|
|
for(ExprNode *i = node; i != NULL; i = i->prev)
|
|
{
|
|
if(i->op == OP_VARIABLE)
|
|
return true;
|
|
|
|
if(i->child != NULL)
|
|
{
|
|
if(right_subtree_has_variable_expr(i->child) == true)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* dump_expr
|
|
*
|
|
* INPUT
|
|
*
|
|
* f - file to dump to
|
|
* node - root node of the (sub-) tree to write to file
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Thorsten Froehlich
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Write an expression tree in inorder notation to a file. Useful for
|
|
* debugging the function parser.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* -
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::dump_expr(FILE *f, ExprNode *node)
|
|
{
|
|
if(node->op == OP_FIRST)
|
|
fprintf(f, "[");
|
|
else
|
|
fprintf(f, "(");
|
|
|
|
fflush(f);
|
|
|
|
for(ExprNode *i = node; i != NULL; i = i->next)
|
|
{
|
|
switch(i->op)
|
|
{
|
|
case OP_CONSTANT:
|
|
fprintf(f, "%f", (float)(i->number));
|
|
break;
|
|
case OP_VARIABLE:
|
|
fprintf(f, "%s", i->variable);
|
|
break;
|
|
case OP_DOT:
|
|
fprintf(f, ".");
|
|
break;
|
|
case OP_MEMBER:
|
|
fprintf(f, "%s", i->variable);
|
|
break;
|
|
case OP_CALL:
|
|
fprintf(f, "fn%d", (int)(i->call.fn));
|
|
break;
|
|
case OP_CMP_EQ:
|
|
fprintf(f, " == ");
|
|
break;
|
|
case OP_CMP_NE:
|
|
fprintf(f, " != ");
|
|
break;
|
|
case OP_CMP_LT:
|
|
fprintf(f, " < ");
|
|
break;
|
|
case OP_CMP_LE:
|
|
fprintf(f, " <= ");
|
|
break;
|
|
case OP_CMP_GT:
|
|
fprintf(f, " > ");
|
|
break;
|
|
case OP_CMP_GE:
|
|
fprintf(f, " >= ");
|
|
break;
|
|
case OP_ADD:
|
|
fprintf(f, " + ");
|
|
break;
|
|
case OP_SUB:
|
|
fprintf(f, " - ");
|
|
break;
|
|
case OP_OR:
|
|
fprintf(f, " | ");
|
|
break;
|
|
case OP_MUL:
|
|
fprintf(f, " * ");
|
|
break;
|
|
case OP_DIV:
|
|
fprintf(f, " / ");
|
|
break;
|
|
case OP_AND:
|
|
fprintf(f, " & ");
|
|
break;
|
|
case OP_POW:
|
|
fprintf(f, " ^ ");
|
|
break;
|
|
case OP_NEG:
|
|
fprintf(f, " -");
|
|
break;
|
|
case OP_NOT:
|
|
fprintf(f, " !");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
fflush(f);
|
|
|
|
if(i->child != NULL)
|
|
dump_expr(f, i->child);
|
|
}
|
|
|
|
if(node->op == OP_FIRST)
|
|
fprintf(f, "]");
|
|
else
|
|
fprintf(f, ")");
|
|
|
|
fflush(f);
|
|
}
|
|
|
|
}
|