blob: 8a71f6d9f9f878ca0359a432fbcb93f3cbaf438f [file] [log] [blame]
/**
* projectM -- Milkdrop-esque visualisation SDK
* Copyright (C)2003-2004 projectM Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* See 'LICENSE.txt' included within this release
*
*/
/* parser.c */
#include <stdio.h>
#include <string>
#include <cstring>
#include <iostream>
#include <stdlib.h>
#include "Common.hpp"
#include "fatal.h"
#include "CustomWave.hpp"
#include "CustomShape.hpp"
#include "Expr.hpp"
#include "Eval.hpp"
#include "Func.hpp"
#include "InitCond.hpp"
#include "Param.hpp"
#include "Parser.hpp"
#include "PerFrameEqn.hpp"
#include "PerPixelEqn.hpp"
#include <map>
#include "ParamUtils.hpp"
#include "wipemalloc.h"
#include <iostream>
#include <sstream>
#include "BuiltinFuncs.hpp"
/* Grabs the next token from the file. The second argument points
to the raw string */
line_mode_t Parser::line_mode;
CustomWave *Parser::current_wave;
CustomShape *Parser::current_shape;
int Parser::string_line_buffer_index;
char Parser::string_line_buffer[STRING_LINE_SIZE];
unsigned int Parser::line_count;
int Parser::per_frame_eqn_count;
int Parser::per_frame_init_eqn_count;
int Parser::last_custom_wave_id;
int Parser::last_custom_shape_id;
char Parser::last_eqn_type[MAX_TOKEN_SIZE];
int Parser::last_token_size;
std::string Parser::lastLinePrefix("");
bool Parser::tokenWrapAroundEnabled(false);
token_t
Parser::parseToken(std::istream & fs, char * string)
{
int c;
int i;
if (string != NULL)
memset(string, 0, MAX_TOKEN_SIZE);
/* Loop until a delimiter is found, or the maximum string size is found */
for (i = 0; i < MAX_TOKEN_SIZE;i++)
{
//c = fgetc(fs);
if (!fs || fs.eof())
c = EOF;
else
c = fs.get();
last_token_size++;
/* If the string line buffer is full, quit */
if (string_line_buffer_index == (STRING_LINE_SIZE - 1))
return tStringBufferFilled;
/* Otherwise add this character to the string line buffer */
string_line_buffer[string_line_buffer_index++] = tolower(c);
/* Now interpret the character */
switch (c)
{
case '+':
return tPlus;
case '-':
return tMinus;
case '%':
return tMod;
case '/':
/* check for line comment here */
if (!fs || fs.eof())
c = EOF;
else
c = fs.get();
if (c == '/')
{
while (true)
{
if (!fs || fs.eof())
c = EOF;
else
c = fs.get();
if (c == EOF)
{
line_mode = UNSET_LINE_MODE;
return tEOF;
}
if (c == '\n')
{
line_mode = UNSET_LINE_MODE;
return tEOL;
}
}
}
/* Otherwise, just a regular division operator */
fs.unget();
return tDiv;
case '*':
return tMult;
case '|':
return tOr;
case '&':
return tAnd;
case '(':
return tLPr;
case ')':
return tRPr;
case '[':
return tLBr;
case ']':
return tRBr;
case '=':
return tEq;
case '\n':
line_count++;
/// @note important hack implemented here to handle "wrap around tokens"
// In particular: "per_frame_1=x=t+1.\nper_frame_2=37 + 5;" implies
// "per_frame_1=x=t+1.37 + 5;". Thus, we have a global flag to determine
// if "token wrap mode" is enabled. If so, we consider a token *continuing*
// to the next line, but only attaching to the token buffer anything following
// the first equals sign on that line.
//
// We can safely assume the next line must be part of the token if we allow the
// semi colon to be a guaranteed delimiter in the grammar for all equation-based lines.
// This IS NO LONGER assumed here. Instead, we check if the next line prefix
// matches with the previous line prefix. If it passes a simple comparison, we wrap around.
if (tokenWrapAroundEnabled)
{
std::ostringstream buffer;
if (PARSE_DEBUG) std::cerr << "token wrap! line " << line_count << std::endl;
while (c != '=')
{
if (!fs || fs.eof())
{
line_count = 1;
line_mode = UNSET_LINE_MODE;
if (PARSE_DEBUG) std::cerr << "token wrap: end of file" << std::endl;
return tEOF;
}
else {
c = fs.get();
if ( c != '=')
buffer << (char)c;
}
}
if (PARSE_DEBUG)
std::cerr << "parseToken: parsed away equal sign, line prefix is \""
<< buffer.str()
<< "\""
<< std::endl;
--i;
if (!wrapsToNextLine(buffer.str())) {
tokenWrapAroundEnabled = false;
int buf_size = (int)buffer.str().length();
// <= to also remove equal sign parsing from stream
for (int k = 0; k <= buf_size; k++) {
if (fs)
fs.unget();
else
abort();
}
return tEOL;
}
break;
}
line_mode = UNSET_LINE_MODE;
return tEOL;
case ',':
return tComma;
case ';':
tokenWrapAroundEnabled = false;
if (PARSE_DEBUG) std::cerr << "token wrap around = false (LINE " << line_count << ")" << std::endl;
return tSemiColon;
case ' ': /* space, skip the character */
i--;
break;
case EOF:
line_count = 1;
line_mode = UNSET_LINE_MODE;
return tEOF;
case '\r':
i--;
break;
default:
if (c == EOF)
std::cerr << "shouldn't happen: " << c << "(LINE " << line_count << ")" << std::endl;
string[i] = tolower(c);
//string[i+1] = 0;
//std::cerr << "string is \n\"" << string << "\"" << std::endl;
}
}
/* String reached maximum length, return special token error */
return tStringTooLong;
}
/* Parse input in the form of "exp, exp, exp, ...)"
Returns a general expression list */
GenExpr **
Parser::parse_prefix_args(std::istream & fs, int num_args, MilkdropPreset * preset)
{
int i, j;
GenExpr ** expr_list; /* List of arguments to function */
GenExpr * gen_expr;
/* Malloc the expression list */
expr_list = (GenExpr**)wipemalloc(sizeof(GenExpr*)*num_args);
/* Malloc failed */
if (expr_list == NULL)
return NULL;
i = 0;
while (i < num_args)
{
//if (PARSE_DEBUG) printf("parse_prefix_args: parsing argument %d...\n", i+1);
/* Parse the ith expression in the list */
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
//if (PARSE_DEBUG) printf("parse_prefix_args: failed to get parameter # %d for function (LINE %d)\n", i+1, line_count);
for (j = 0; j < i; j++)
delete expr_list[j];
free(expr_list);
expr_list = NULL;
return NULL;
}
/* Assign entry in expression list */
expr_list[i++] = gen_expr;
}
//if (PARSE_DEBUG) printf("parse_prefix_args: finished parsing %d arguments (LINE %d)\n", num_args, line_count);
/* Finally, return the resulting expression list */
return expr_list;
}
/* Parses a comment at the top of the file. Stops when left bracket is found */
int
Parser::parse_top_comment(std::istream & fs)
{
char string[MAX_TOKEN_SIZE];
token_t token;
/* Process tokens until left bracket is found */
while ((token = parseToken(fs, string)) != tLBr)
{
if (token == tEOF)
return PROJECTM_PARSE_ERROR;
}
/* Done, return success */
return PROJECTM_SUCCESS;
}
/* Right Bracket is parsed by this function.
puts a new string into name */
int
Parser::parse_preset_name(std::istream & fs, char * name)
{
token_t token;
if (name == NULL)
return PROJECTM_FAILURE;
if ((token = parseToken(fs, name)) != tRBr)
return PROJECTM_PARSE_ERROR;
return PROJECTM_SUCCESS;
}
/* Parses per pixel equations */
int
Parser::parse_per_pixel_eqn(std::istream & fs, MilkdropPreset * preset, char * init_string)
{
char string[MAX_TOKEN_SIZE];
GenExpr * gen_expr;
if (init_string != 0)
{
strncpy(string, init_string, strlen(init_string));
}
else
{
if (parseToken(fs, string) != tEq)
{ /* parse per pixel operator name */
return PROJECTM_PARSE_ERROR;
}
}
/* Parse right side of equation as an expression */
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
return PROJECTM_PARSE_ERROR;
}
/* Add the per pixel equation */
if (preset->add_per_pixel_eqn(string, gen_expr) < 0)
{
if (PARSE_DEBUG)
{
}
delete gen_expr;
return PROJECTM_PARSE_ERROR;
}
return PROJECTM_SUCCESS;
}
/* Parses an equation line, this function is way too big, should add some helper functions */
int
Parser::parse_line(std::istream & fs, MilkdropPreset * preset)
{
char eqn_string[MAX_TOKEN_SIZE];
token_t token;
InitCond * init_cond;
PerFrameEqn * per_frame_eqn;
/* Clear the string line buffer */
memset(string_line_buffer, 0, STRING_LINE_SIZE);
string_line_buffer_index = 0;
tokenWrapAroundEnabled = false;
token = parseToken( fs, eqn_string );
switch (token)
{
/* Invalid Cases */
case tRBr:
case tLPr:
case tRPr:
case tComma:
case tLBr:
case tPlus:
case tMinus:
case tMod:
case tMult:
case tOr:
case tAnd:
case tDiv:
if (PARSE_DEBUG) std::cerr << "parse_line: invalid token found at start of line (LINE "
<< line_count << ")" << std::endl;
/* Invalid token found, return a parse error */
return PROJECTM_PARSE_ERROR;
case tEOL: /* Empty line */
line_mode = UNSET_LINE_MODE;
return PROJECTM_SUCCESS;
case tEOF: /* End of File */
line_mode = UNSET_LINE_MODE;
line_count = 1;
tokenWrapAroundEnabled = false;
return EOF;
case tSemiColon: /* Indicates end of expression */
tokenWrapAroundEnabled = false;
return PROJECTM_SUCCESS;
/* Valid Case, either an initial condition or equation should follow */
case tEq:
lastLinePrefix = std::string(eqn_string);
if (PARSE_DEBUG) std::cout << "last line prefix = \"" << eqn_string << "\"" << std::endl;
// std::cerr << "parse_line: tEQ case, fs.peek()=\'" << fs.peek() << "\'" << std::endl;
if (!fs)
return PROJECTM_PARSE_ERROR;
// char z = fs.get();
char tmpChar;
if ((tmpChar = fs.get()) == '\n') {
tokenWrapAroundEnabled = false;
return PROJECTM_PARSE_ERROR;
} else if (tmpChar == '\r') {
tokenWrapAroundEnabled = false;
return PROJECTM_PARSE_ERROR;
} else
fs.unget();
/* CASE: WARP CODE */
if (!strncmp(eqn_string, WARP_STRING, WARP_STRING_LENGTH))
{
//std::cout << "parsing warp string block\n" << std::endl;
parse_string_block(fs, &preset->presetOutputs().warpShader.programSource);
return PROJECTM_SUCCESS;
}
/* CASE: COMPOSITE CODE */
if (!strncmp(eqn_string, COMPOSITE_STRING, COMPOSITE_STRING_LENGTH))
{
//std::cout << "parsing composite string block\n" << std::endl;
parse_string_block(fs, &preset->presetOutputs().compositeShader.programSource);
return PROJECTM_SUCCESS;
}
/* CASE: PER FRAME INIT EQUATION */
if (!strncmp(eqn_string, PER_FRAME_INIT_STRING, PER_FRAME_INIT_STRING_LENGTH))
{
tokenWrapAroundEnabled = true;
//if (PARSE_DEBUG) printf("parse_line: per frame init equation found...(LINE %d)\n", line_count);
/* Parse the per frame equation */
if ((init_cond = parse_per_frame_init_eqn(fs, preset, NULL)) == NULL)
{
//if (PARSE_DEBUG) printf("parse_line: per frame init equation parsing failed (LINE %d)\n", line_count);
tokenWrapAroundEnabled = false;
return PROJECTM_PARSE_ERROR;
}
/* Insert the equation in the per frame equation tree */
preset->per_frame_init_eqn_tree.insert(std::make_pair(init_cond->param->name, init_cond));
line_mode = PER_FRAME_INIT_LINE_MODE;
return PROJECTM_SUCCESS;
}
/* Per frame equation case */
if (!strncmp(eqn_string, PER_FRAME_STRING, PER_FRAME_STRING_LENGTH))
{
tokenWrapAroundEnabled = true;
/* Sometimes per frame equations are implicitly defined without the
per_frame_ prefix. This informs the parser that one could follow */
line_mode = PER_FRAME_LINE_MODE;
//if (PARSE_DEBUG) printf("parse_line: per frame equation found...(LINE %d)\n", line_count);
/* Parse the per frame equation */
if ((per_frame_eqn = parse_per_frame_eqn(fs, ++per_frame_eqn_count, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_line: per frame equation parsing failed (LINE %d)\n", line_count);
tokenWrapAroundEnabled = false;
return PROJECTM_PARSE_ERROR;
}
/* Insert the equation in the per frame equation tree */
preset->per_frame_eqn_tree.push_back(per_frame_eqn);
return PROJECTM_SUCCESS;
}
/* Wavecode initial condition case */
if (!strncmp(eqn_string, WAVECODE_STRING, WAVECODE_STRING_LENGTH))
{
line_mode = CUSTOM_WAVE_WAVECODE_LINE_MODE;
return parse_wavecode(eqn_string, fs, preset);
}
/* Custom Wave Prefix */
if ( (!strncmp(eqn_string, WAVE_STRING, WAVE_STRING_LENGTH))
&& ((eqn_string[5] >= 48) && (eqn_string[5] <= 57)))
{
tokenWrapAroundEnabled = true;
// if (PARSE_DEBUG) printf("parse_line wave prefix found: \"%s\"\n", eqn_string);
return parse_wave(eqn_string, fs, preset);
}
/* Shapecode initial condition case */
if (!strncmp(eqn_string, SHAPECODE_STRING, SHAPECODE_STRING_LENGTH))
{
line_mode = CUSTOM_SHAPE_SHAPECODE_LINE_MODE;
if (PARSE_DEBUG) printf("parse_line: shapecode prefix found: \"%s\"\n", eqn_string);
return parse_shapecode(eqn_string, fs, preset);
}
/* Custom Shape Prefix */
if ( (!strncmp(eqn_string, SHAPE_STRING, SHAPE_STRING_LENGTH))
&& ((eqn_string[6] >= 48) && (eqn_string[6] <= 57)))
{
tokenWrapAroundEnabled = true;
if (PARSE_DEBUG) printf("parse_line shape prefix found: \"%s\"\n", eqn_string);
return parse_shape(eqn_string, fs, preset);
}
/* Per pixel equation case */
if (!strncmp(eqn_string, PER_PIXEL_STRING, PER_PIXEL_STRING_LENGTH))
{
tokenWrapAroundEnabled = true;
line_mode = PER_PIXEL_LINE_MODE;
if (parse_per_pixel_eqn(fs, preset, 0) < 0)
{
tokenWrapAroundEnabled = false;
return PROJECTM_PARSE_ERROR;
}
if (PARSE_DEBUG) printf("parse_line: finished parsing per pixel equation (LINE %d)\n", line_count);
return PROJECTM_SUCCESS;
}
/* Sometimes equations are written implicitly in milkdrop files, in the form
per_frame_1 = p1 = eqn1; p2 = eqn2; p3 = eqn3;..;
which is analagous to:
per_frame_1 = p1 = eqn1; per_frame_2 = p2 = eqn2; per_frame_3 = p3 = eqn3; ...;
The following line mode hack allows such implicit declaration of the
prefix that specifies the equation type. An alternative method
may be to associate each equation line as list of equations separated
by semicolons (and a new line ends the list). Instead, however, a global
variable called "line_mode" specifies the last type of equation found,
and bases any implicitly typed input on this fact
Note added by Carmelo Piccione (carmelo.piccione@gmail.com) 10/19/03
*/
/* Per frame line mode previously, try to parse the equation implicitly */
if (line_mode == PER_FRAME_LINE_MODE)
{
tokenWrapAroundEnabled = true;
if ((per_frame_eqn = parse_implicit_per_frame_eqn(fs, eqn_string, ++per_frame_eqn_count, preset)) == NULL)
{
tokenWrapAroundEnabled = false;
return PROJECTM_PARSE_ERROR;
}
/* Insert the equation in the per frame equation tree */
preset->per_frame_eqn_tree.push_back(per_frame_eqn);
return PROJECTM_SUCCESS;
}
else if (line_mode == PER_FRAME_INIT_LINE_MODE)
{
tokenWrapAroundEnabled = true;
if (PARSE_DEBUG) printf("parse_line: parsing implicit per frame init eqn)\n");
if ((init_cond = parse_per_frame_init_eqn(fs, preset, NULL)) == NULL)
{
tokenWrapAroundEnabled = false;
return PROJECTM_PARSE_ERROR;
}
++per_frame_init_eqn_count;
/* Insert the equation in the per frame equation tree */
preset->per_frame_init_eqn_tree.insert(std::make_pair(init_cond->param->name, init_cond));
return PROJECTM_SUCCESS;
}
else if (line_mode == PER_PIXEL_LINE_MODE)
{
tokenWrapAroundEnabled = true;
if (PARSE_DEBUG) printf("parse_line: implicit per pixel eqn (LINE %d)\n", line_count);
return parse_per_pixel_eqn(fs, preset, eqn_string);
}
else if (line_mode == CUSTOM_WAVE_PER_POINT_LINE_MODE)
{
tokenWrapAroundEnabled = true;
if (PARSE_DEBUG) printf("parse_line: implicit cwave ppoint eqn found (LINE %d)\n", line_count);
//int len = strlen(eqn_string);
if (parse_wave_helper(fs, preset, last_custom_wave_id, last_eqn_type, eqn_string) < 0)
{
if (PARSE_DEBUG) printf("parse_line: failed to parse an implicit custom wave per point eqn\n");
return PROJECTM_FAILURE;
}
return PROJECTM_SUCCESS;
}
else if (line_mode == CUSTOM_WAVE_PER_FRAME_LINE_MODE)
{
tokenWrapAroundEnabled = true;
//Added by PJS. I hope I did it right
CustomWave * custom_wave;
/* Retrieve custom shape associated with this id */
if ((custom_wave = MilkdropPreset::find_custom_object(last_custom_wave_id, preset->customWaves)) == NULL)
return PROJECTM_FAILURE;
return parse_wave_per_frame_eqn(fs, custom_wave, preset);
}
else if (line_mode == CUSTOM_WAVE_WAVECODE_LINE_MODE)
{
if (PARSE_DEBUG) printf("unsupported line mode: CUSTOM_WAVE_WAVECODE_LINE_MODE\n");
return PROJECTM_FAILURE;
}
else if (line_mode == CUSTOM_SHAPE_SHAPECODE_LINE_MODE)
{
if (PARSE_DEBUG) printf("unsupported line mode: CUSTOM_SHAPE_SHAPECODE_LINE_MODE\n");
return PROJECTM_FAILURE;
}
else if (line_mode == CUSTOM_SHAPE_PER_FRAME_LINE_MODE)
{
tokenWrapAroundEnabled = true;
CustomShape * custom_shape;
/* Retrieve custom shape associated with this id */
if ((custom_shape = MilkdropPreset::find_custom_object(last_custom_shape_id, preset->customShapes)) == NULL)
return PROJECTM_FAILURE;
return parse_shape_per_frame_eqn(fs, custom_shape, preset);
}
else if (line_mode == CUSTOM_SHAPE_PER_FRAME_INIT_LINE_MODE)
{
tokenWrapAroundEnabled = true;
CustomShape * custom_shape;
/* Retrieve custom shape associated with this id */
if ((custom_shape = preset->find_custom_object(last_custom_shape_id, preset->customShapes)) == NULL)
return PROJECTM_FAILURE;
return parse_shape_per_frame_init_eqn(fs, custom_shape, preset);
}
if (PARSE_DEBUG) printf("parse_line: found initial condition: name = \"%s\" (LINE %d)\n", eqn_string, line_count);
/* Evaluate the initial condition */
if ((init_cond = parse_init_cond(fs, eqn_string, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_line: failed to parse initial condition (LINE %d)\n", line_count);
return PROJECTM_PARSE_ERROR;
}
/* Add equation to initial condition tree */
preset->init_cond_tree.insert(std::make_pair(init_cond->param->name, init_cond));
/* Finished with initial condition line */
// if (PARSE_DEBUG) printf("parse_line: initial condition parsed successfully\n");
return PROJECTM_SUCCESS;
/* END INITIAL CONDITIONING PARSING */
default: /* an uncaught type or an error has occurred */
if (PARSE_DEBUG) printf("parse_line: uncaught case, token val = %d\n", token);
return PROJECTM_PARSE_ERROR;
}
/* Because of the default in the case statement,
control flow should never actually reach here */
return PROJECTM_PARSE_ERROR;
}
/* Parses a general expression, this function is the meat of the parser */
GenExpr *
Parser::parse_gen_expr ( std::istream & fs, TreeExpr * tree_expr, MilkdropPreset * preset)
{
int i;
char string[MAX_TOKEN_SIZE];
token_t token;
GenExpr * gen_expr;
float val;
Param * param = NULL;
Func * func;
GenExpr ** expr_list;
switch (token = parseToken(fs,string))
{
/* Left Parentice Case */
case tLPr:
//std::cerr << "token before tLPr:" << string << std::endl;
/* CASE 1 (Left Parentice): See if the previous string before this parentice is a function name */
if ((func = BuiltinFuncs::find_func(string)) != NULL)
{
if (PARSE_DEBUG)
{
std::cerr << "parse_gen_expr: found prefix function (name = \""
<< func->getName()
<< "\") (LINE " << line_count << ")"
<< std::endl;
}
/* Parse the functions arguments */
if ((expr_list = parse_prefix_args(fs, func->getNumArgs(), preset)) == NULL)
{
if (PARSE_DEBUG)
{
std::cerr << "parse_prefix_args: failed to generate an expresion list! (LINE "
<< line_count << ")"
<< std::endl;
}
if ( tree_expr != NULL )
{
delete tree_expr;
}
return NULL;
}
/* Convert function to expression */
if ((gen_expr = GenExpr::prefun_to_expr((float (*)(void *))func->func_ptr, expr_list, func->getNumArgs())) == NULL)
{
if (PARSE_DEBUG)
printf("parse_prefix_args: failed to convert prefix function to general expression (LINE %d) \n", line_count);
if (tree_expr)
delete tree_expr;
for (i = 0; i < func->getNumArgs();i++)
delete expr_list[i];
free(expr_list);
expr_list = NULL;
return NULL;
}
token = parseToken(fs, string);
if (*string != 0)
{
if (PARSE_DEBUG) printf("parse_prefix_args: empty string expected, but not found...(LINE %d)\n", line_count);
/* continue anyway for now, could be implicit multiplication */
}
return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);
}
/* Case 2: (Left Parentice), a string coupled with a left parentice. Either an error or implicit
multiplication operator. For now treat it as an error */
if (*string != 0)
{
std::cerr << "token prefix is " << *string << std::endl;
if (PARSE_DEBUG) printf("parse_gen_expr: implicit multiplication case unimplemented!\n");
if (tree_expr)
delete tree_expr;
return NULL;
}
/* CASE 3 (Left Parentice): the following is enclosed parentices to change order
of operations. So we create a new expression tree */
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_gen_expr: found left parentice, but failed to create new expression tree \n");
if (tree_expr)
delete tree_expr;
return NULL;
}
if (PARSE_DEBUG) printf("parse_gen_expr: finished enclosed expression tree...\n");
token = parseToken(fs, string);
return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);
/* Plus is a prefix operator check */
case tPlus:
if (*string == 0)
{
if (PARSE_DEBUG) printf("parse_gen_expr: plus used as prefix (LINE %d)\n", line_count);
/* Treat prefix plus as implict 0 preceding operator */
gen_expr = GenExpr::const_to_expr(0);
return parse_infix_op(fs, tPositive, insert_gen_expr(gen_expr, &tree_expr), preset);
}
/* Minus is a prefix operator check */
case tMinus:
if (*string == 0)
{
/* Use the negative infix operator, but first add an implicit zero to the operator tree */
gen_expr = GenExpr::const_to_expr(0);
//return parse_gen_expr(fs, insert_gen_expr(gen_expr, &tree_expr), preset);
return parse_infix_op(fs, tNegative, insert_gen_expr(gen_expr, &tree_expr), preset);
}
/* All the following cases are strings followed by an infix operator or terminal */
case tRPr:
case tEOL:
case tEOF:
case tSemiColon:
case tComma:
/* CASE 1 (terminal): string is empty, but not null. Not sure if this will actually happen
any more. */
if (*string == 0)
{
if (PARSE_DEBUG) printf("parse_gen_expr: empty string coupled with terminal (LINE %d) \n", line_count);
return parse_infix_op(fs, token, tree_expr, preset);
}
default:
/* CASE 0: Empty string, parse error */
if (*string == 0)
{
if (tree_expr)
delete tree_expr;
return NULL;
}
/* CASE 1: Check if string is a just a floating point number */
if (string_to_float(string, &val) != PROJECTM_PARSE_ERROR)
{
if ((gen_expr = GenExpr::const_to_expr(val)) == NULL)
{
if (tree_expr)
delete tree_expr;
return NULL;
}
/* Parse the rest of the line */
return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);
}
/* CASE 4: custom shape variable */
if (current_shape != NULL)
{
if ((param = ParamUtils::find<ParamUtils::NO_CREATE>(std::string(string), &current_shape->param_tree)) == NULL)
{
if ((param = preset->builtinParams.find_builtin_param(std::string(string))) == NULL)
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(std::string(string), &current_shape->param_tree)) == NULL)
{
if (tree_expr)
delete tree_expr;
return NULL;
}
}
if (PARSE_DEBUG)
{
std::cerr << "parse_gen_expr: custom shape parameter (name = "
<< param->name << ")"
<< std::endl;
}
/* Convert parameter to an expression */
if ((gen_expr = GenExpr::param_to_expr(param)) == NULL)
{
delete tree_expr;
return NULL;
}
//if (PARSE_DEBUG) printf("converted to expression (LINE %d)\n", line_count);
/* Parse the rest of the line */
return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);
}
/* CASE 5: custom wave variable */
if (current_wave != NULL)
{
if ((param = ParamUtils::find<ParamUtils::NO_CREATE>(std::string(string), &current_wave->param_tree)) == NULL)
{
if ((param = preset->builtinParams.find_builtin_param(std::string(string))) == NULL)
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(std::string(string), &current_wave->param_tree)) == NULL)
{
if (tree_expr)
delete tree_expr;
return NULL;
}
}
assert(param);
if (PARSE_DEBUG)
{
std::cerr << "parse_gen_expr: custom wave parameter (name = " << param->name << ")" << std::endl;
}
/* Convert parameter to an expression */
if ((gen_expr = GenExpr::param_to_expr(param)) == NULL)
{
delete tree_expr;
return NULL;
}
//if (PARSE_DEBUG) printf("converted to expression (LINE %d)\n", line_count);
/* Parse the rest of the line */
return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);
}
/* CASE 6: regular parameter. Will be created if necessary and the string has no invalid characters */
if ((param = ParamUtils::find(string, &preset->builtinParams, &preset->user_param_tree)) != NULL)
{
if (PARSE_DEBUG)
{
std::cerr << "parse_gen_expr: parameter (name = \"" << param->name << "\")..." << std::endl;
}
/* Convert parameter to an expression */
if ((gen_expr = GenExpr::param_to_expr(param)) == NULL)
{
delete tree_expr;
return NULL;
}
//if (PARSE_DEBUG) printf("converted to expression (LINE %d)\n", line_count);
/* Parse the rest of the line */
return parse_infix_op(fs, token, insert_gen_expr(gen_expr, &tree_expr), preset);
}
/* CASE 7: Bad string, give up */
if (PARSE_DEBUG)
{
printf( "parse_gen_expr: syntax error [string = \"%s\"] (LINE %d)\n", string, line_count);
}
if (tree_expr)
delete tree_expr;
return NULL;
}
}
/* Inserts expressions into tree according to operator precedence.
If root is null, a new tree is created, with infix_op as only element */
TreeExpr *
Parser::insert_infix_op(InfixOp * infix_op, TreeExpr **root)
{
TreeExpr * new_root;
/* Sanity check */
if (infix_op == NULL)
return NULL;
/* The root is null, so make this operator
the new root */
if (*root == NULL)
{
new_root = new TreeExpr(infix_op, NULL, NULL, NULL);
*root = new_root;
return new_root;
}
/* The root node is not an infix function,
so we make this infix operator the new root */
if ((*root)->infix_op == NULL)
{
new_root = new TreeExpr(infix_op, NULL, *root, NULL);
(*root) = new_root;
return new_root;
}
/* The root is an infix function. If the precedence
of the item to be inserted is greater than the root's
precedence, then make gen_expr the root */
if (infix_op->precedence >= (*root)->infix_op->precedence)
{
new_root = new TreeExpr(infix_op, NULL, *root, NULL);
(*root) = new_root;
return new_root;
}
/* If control flow reaches here, use a recursive helper
with the knowledge that the root is higher precedence
than the item to be inserted */
insert_infix_rec(infix_op, *root);
return *root;
}
TreeExpr *
Parser::insert_gen_expr(GenExpr * gen_expr, TreeExpr ** root)
{
TreeExpr * new_root;
/* If someone foolishly passes a null
pointer to insert, return the original tree */
if (gen_expr == NULL)
{
return *root;
}
/* If the root is null, generate a new expression tree,
using the passed expression as the root element */
if (*root == NULL)
{
new_root = new TreeExpr(NULL, gen_expr, NULL, NULL);
*root = new_root;
return new_root;
}
/* Otherwise. the new element definitely will not replace the current root.
Use a recursive helper function to do insertion */
insert_gen_rec(gen_expr, *root);
return *root;
}
/* A recursive helper function to insert general expression elements into the operator tree */
int
Parser::insert_gen_rec(GenExpr * gen_expr, TreeExpr * root)
{
/* Trivial Case: root is null */
if (root == NULL)
{
//if (PARSE_DEBUG) printf("insert_gen_rec: root is null, returning failure\n");
return PROJECTM_FAILURE;
}
/* The current node's left pointer is null, and this
current node is an infix operator, so insert the
general expression at the left pointer */
if ((root->left == NULL) && (root->infix_op != NULL))
{
root->left = new TreeExpr(NULL, gen_expr, NULL, NULL);
return PROJECTM_SUCCESS;
}
/* The current node's right pointer is null, and this
current node is an infix operator, so insert the
general expression at the right pointer */
if ((root->right == NULL) && (root->infix_op != NULL))
{
root->right = new TreeExpr(NULL, gen_expr, NULL, NULL);
return PROJECTM_SUCCESS;
}
/* Otherwise recurse down to the left. If
this succeeds then return. If it fails, try
recursing down to the right */
if (insert_gen_rec(gen_expr, root->left) == PROJECTM_FAILURE)
return insert_gen_rec(gen_expr, root->right);
/* Impossible for control flow to reach here, but in
the world of C programming, who knows... */
if (PARSE_DEBUG) printf("insert_gen_rec: should never reach here!\n");
return PROJECTM_FAILURE;
}
/* A recursive helper function to insert infix arguments by operator precedence */
int
Parser::insert_infix_rec(InfixOp * infix_op, TreeExpr * root)
{
/* Shouldn't happen, implies a parse error */
if (root == NULL)
return PROJECTM_FAILURE;
/* Also shouldn't happen, also implies a (different) parse error */
if (root->infix_op == NULL)
return PROJECTM_FAILURE;
/* Left tree is empty, attach this operator to it.
I don't think this will ever happen */
if (root->left == NULL)
{
root->left = new TreeExpr(infix_op, NULL, root->left, NULL);
return PROJECTM_SUCCESS;
}
/* Right tree is empty, attach this operator to it */
if (root->right == NULL)
{
root->right = new TreeExpr(infix_op, NULL, root->right, NULL);
return PROJECTM_SUCCESS;
}
/* The left element can now be ignored, since there is no way for this
operator to use those expressions */
/* If the right element is not an infix operator,
then insert the expression here, attaching the old right branch
to the left of the new expression */
if (root->right->infix_op == NULL)
{
root->right = new TreeExpr(infix_op, NULL, root->right, NULL);
return PROJECTM_SUCCESS;
}
/* Traverse deeper if the inserting operator precedence is less than the
the root's right operator precedence */
if (infix_op->precedence < root->right->infix_op->precedence)
return insert_infix_rec(infix_op, root->right);
/* Otherwise, insert the operator here */
root->right = new TreeExpr(infix_op, NULL, root->right, NULL);
return PROJECTM_SUCCESS;
}
/* Parses an infix operator */
GenExpr *
Parser::parse_infix_op(std::istream & fs, token_t token, TreeExpr * tree_expr, MilkdropPreset * preset)
{
GenExpr * gen_expr;
switch (token)
{
/* All the infix operators */
case tPlus:
if (PARSE_DEBUG) printf("parse_infix_op: found addition operator (LINE %d)\n", line_count);
if (PARSE_DEBUG) std::cerr << "WRAP AROUND IS " << tokenWrapAroundEnabled << std::endl;
return parse_gen_expr(fs, insert_infix_op(Eval::infix_add, &tree_expr), preset);
case tMinus:
if (PARSE_DEBUG) printf("parse_infix_op: found subtraction operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_minus, &tree_expr), preset);
case tMult:
if (PARSE_DEBUG) printf("parse_infix_op: found multiplication operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_mult, &tree_expr), preset);
case tDiv:
if (PARSE_DEBUG) printf("parse_infix_op: found division operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_div, &tree_expr), preset);
case tMod:
if (PARSE_DEBUG) printf("parse_infix_op: found modulo operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_mod, &tree_expr), preset);
case tOr:
if (PARSE_DEBUG) printf("parse_infix_op: found bitwise or operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_or, &tree_expr), preset);
case tAnd:
if (PARSE_DEBUG) printf("parse_infix_op: found bitwise and operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_and, &tree_expr), preset);
case tPositive:
if (PARSE_DEBUG) printf("parse_infix_op: found positive operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_positive, &tree_expr), preset);
case tNegative:
if (PARSE_DEBUG) printf("parse_infix_op: found negative operator (LINE %d)\n", line_count);
return parse_gen_expr(fs, insert_infix_op(Eval::infix_negative, &tree_expr), preset);
case tEOL:
case tEOF:
case tSemiColon:
case tRPr:
case tComma:
if (PARSE_DEBUG) printf("parse_infix_op: terminal found (LINE %d)\n", line_count);
gen_expr = new GenExpr(TREE_T, (void*)tree_expr);
assert(gen_expr);
return gen_expr;
default:
if (PARSE_DEBUG) printf("parse_infix_op: operator or terminal expected, but not found (LINE %d)\n", line_count);
delete tree_expr;
return NULL;
}
/* Will never happen */
return NULL;
}
/* Parses an integer, checks for +/- prefix */
int
Parser::parse_int(std::istream & fs, int * int_ptr)
{
char string[MAX_TOKEN_SIZE];
token_t token;
int sign;
char * end_ptr = (char*)" ";
token = parseToken(fs, string);
switch (token)
{
case tMinus:
sign = -1;
token = parseToken(fs, string);
break;
case tPlus:
sign = 1;
token = parseToken(fs, string);
break;
default:
sign = 1;
break;
}
if (string[0] == 0)
return PROJECTM_PARSE_ERROR;
/* Convert the string to an integer. *end_ptr
should end up pointing to null terminator of 'string'
if the conversion was successful. */
// printf("STRING: \"%s\"\n", string);
(*int_ptr) = sign*strtol(string, &end_ptr, 10);
/* If end pointer is a return character or null terminator, all is well */
if ((*end_ptr == '\r') || (*end_ptr == '\0'))
return PROJECTM_SUCCESS;
return PROJECTM_PARSE_ERROR;
}
/* Parses a floating point number */
int
Parser::string_to_float(char * string, float * float_ptr)
{
char ** error_ptr;
if (*string == 0)
return PROJECTM_PARSE_ERROR;
error_ptr = (char**)wipemalloc(sizeof(char**));
(*float_ptr) = strtod(string, error_ptr);
/* These imply a succesful parse of the string */
if ((**error_ptr == '\0') || (**error_ptr == '\r'))
{
free(error_ptr);
error_ptr = NULL;
return PROJECTM_SUCCESS;
}
(*float_ptr) = 0;
free(error_ptr);
error_ptr = NULL;
return PROJECTM_PARSE_ERROR;
}
/* Parses a floating point number */
int
Parser::parse_float(std::istream & fs, float * float_ptr)
{
char string[MAX_TOKEN_SIZE];
char ** error_ptr;
token_t token;
int sign;
error_ptr =(char**) wipemalloc(sizeof(char**));
token = parseToken(fs, string);
switch (token)
{
case tMinus:
sign = -1;
token = parseToken(fs, string);
break;
case tPlus:
sign = 1;
token = parseToken(fs, string);
break;
default:
sign = 1;
}
if (string[0] == 0)
{
free(error_ptr);
error_ptr = NULL;
return PROJECTM_PARSE_ERROR;
}
(*float_ptr) = sign*strtod(string, error_ptr);
/* No conversion was performed */
if ((**error_ptr == '\0') || (**error_ptr == '\r'))
{
free(error_ptr);
error_ptr = NULL;
return PROJECTM_SUCCESS;
}
if (PARSE_DEBUG) printf("parse_float: float conversion failed for string \"%s\"\n", string);
(*float_ptr) = 0;
free(error_ptr);
error_ptr = NULL;
return PROJECTM_PARSE_ERROR;
}
/* Parses a per frame equation. That is, interprets a stream of data as a per frame equation */
PerFrameEqn *
Parser::parse_per_frame_eqn(std::istream & fs, int index, MilkdropPreset * preset)
{
char string[MAX_TOKEN_SIZE];
Param * param;
PerFrameEqn * per_frame_eqn;
GenExpr * gen_expr;
if (parseToken(fs, string) != tEq)
{
if (PARSE_DEBUG) printf("parse_per_frame_eqn: no equal sign after string \"%s\" (LINE %d)\n", string, line_count);
return NULL;
}
/* Find the parameter associated with the string, create one if necessary */
if ((param = ParamUtils::find(string, &preset->builtinParams, &preset->user_param_tree)) == NULL)
{
return NULL;
}
if (PARSE_DEBUG) std::cerr << "parse_per_frame_eqn: parameter \"" << param->name << "\" retrieved (LINE" << line_count << ")" << std::endl;
/* Make sure parameter is writable */
if (param->flags & P_FLAG_READONLY)
{
if (PARSE_DEBUG) std::cerr << "parse_per_frame_eqn: parameter \"" << param->name << "\" %s is marked as read only (LINE " << line_count << ")" << std::endl;
return NULL;
}
/* Parse right side of equation as an expression */
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_per_frame_eqn: equation evaluated to null (LINE %d)\n", line_count);
return NULL;
}
if (PARSE_DEBUG) printf("parse_per_frame_eqn: finished per frame equation evaluation (LINE %d)\n", line_count);
/* Create a new per frame equation */
if ((per_frame_eqn = new PerFrameEqn(index, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_per_frame_eqn: failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
return NULL;
}
if (PARSE_DEBUG) printf("parse_per_frame_eqn: per_frame eqn parsed succesfully\n");
return per_frame_eqn;
}
/* Parses an 'implicit' per frame equation. That is, interprets a stream of data as a per frame equation without a prefix */
PerFrameEqn *
Parser::parse_implicit_per_frame_eqn(std::istream & fs, char * param_string, int index, MilkdropPreset * preset)
{
Param * param;
PerFrameEqn * per_frame_eqn;
GenExpr * gen_expr;
if (fs == NULL)
return NULL;
if (param_string == NULL)
return NULL;
if (preset == NULL)
return NULL;
//rintf("param string: %s\n", param_string);
/* Find the parameter associated with the string, create one if necessary */
if ((param = ParamUtils::find(param_string, &preset->builtinParams, &preset->user_param_tree)) == NULL)
{
return NULL;
}
//printf("parse_implicit_per_frame_eqn: param is %s\n", param->name);
/* Make sure parameter is writable */
if (param->flags & P_FLAG_READONLY)
{
if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: parameter %s is marked as read only (LINE %d)\n", param->name.c_str(), line_count);
return NULL;
}
/* Parse right side of equation as an expression */
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: equation evaluated to null (LINE %d)\n", line_count);
return NULL;
}
if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: finished per frame equation evaluation (LINE %d)\n", line_count);
/* Create a new per frame equation */
if ((per_frame_eqn = new PerFrameEqn(index, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
return NULL;
}
if (PARSE_DEBUG) printf("parse_implicit_per_frame_eqn: per_frame eqn parsed succesfully\n");
return per_frame_eqn;
}
/* Parses an initial condition */
InitCond *
Parser::parse_init_cond(std::istream & fs, char * name, MilkdropPreset * preset)
{
Param * param;
CValue init_val;
InitCond * init_cond;
if (name == NULL)
return NULL;
if (preset == NULL)
return NULL;
/* Search for the paramater in the database, creating it if necessary */
if ((param = ParamUtils::find(name, &preset->builtinParams, &preset->user_param_tree)) == NULL)
{
return NULL;
}
if (PARSE_DEBUG) printf("parse_init_cond: parameter = \"%s\" (LINE %d)\n", param->name.c_str(), line_count);
if (param->flags & P_FLAG_READONLY)
{
if (PARSE_DEBUG) printf("parse_init_cond: builtin parameter \"%s\" marked as read only!\n", param->name.c_str());
return NULL;
}
/* At this point, a parameter has been created or was found
in the database. */
if (PARSE_DEBUG) printf("parsed_init_cond: parsing initial condition value... (LINE %d)\n", line_count);
/* integer value (boolean is an integer in C) */
if ( (param->type == P_TYPE_BOOL))
{
int bool_test;
if ((parse_int(fs, &bool_test)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_init_cond: error parsing integer!\n");
return NULL;
}
init_val.bool_val = bool_test;
}
else if ((param->type == P_TYPE_INT))
{
if ((parse_int(fs, (int*)&init_val.int_val)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_init_cond: error parsing integer!\n");
return NULL;
}
}
/* float value */
else if (param->type == P_TYPE_DOUBLE)
{
if ((parse_float(fs, (float*)&init_val.float_val)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_init_cond: error parsing float!\n");
return NULL;
}
}
/* Unknown value */
else
{
if (PARSE_DEBUG) printf("parse_init_cond: unknown parameter type!\n");
return NULL;
}
/* Create new initial condition */
if ((init_cond = new InitCond(param, init_val)) == NULL)
{
if (PARSE_DEBUG) printf("parse_init_cond: new_init_cond failed!\n");
return NULL;
}
/* Finished */
return init_cond;
}
void
Parser::parse_string_block(std::istream & fs, std::string * out_string)
{
char name[MAX_TOKEN_SIZE];
token_t token;
std::set<char> skipList;
skipList.insert('`');
readStringUntil(fs, out_string, false, skipList);
//std::cout << "out_string:\n " << *out_string << "\n" << std::endl;
}
InitCond *
Parser::parse_per_frame_init_eqn(std::istream & fs, MilkdropPreset * preset, std::map<std::string,Param*> * database)
{
char name[MAX_TOKEN_SIZE];
Param * param = NULL;
CValue init_val;
InitCond * init_cond;
GenExpr * gen_expr;
float val;
token_t token;
if (preset == NULL)
return NULL;
if (fs == NULL)
return NULL;
if ((token = parseToken(fs, name)) != tEq)
return NULL;
/* If a database was specified,then use ParamUtils::find_db instead */
if ((database != NULL) && ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(name, database)) == NULL))
{
return NULL;
}
/* Otherwise use the builtin parameter and user databases. This is confusing. Sorry. */
if ((param == NULL) && ((param = ParamUtils::find(name, &preset->builtinParams, &preset->user_param_tree)) == NULL))
{
return NULL;
}
if (PARSE_DEBUG) printf("parse_per_frame_init_eqn: parameter = \"%s\" (LINE %d)\n", param->name.c_str(), line_count);
if (param->flags & P_FLAG_READONLY)
{
if (PARSE_DEBUG) printf("pars_per_frame_init_eqn: builtin parameter \"%s\" marked as read only!\n", param->name.c_str());
return NULL;
}
/* At this point, a parameter has been created or was found
in the database. */
if (PARSE_DEBUG) printf("parse_per_frame_init_eqn: parsing right hand side of per frame init equation.. (LINE %d)\n", line_count);
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_per_frame_init_eqn: failed to parse general expresion!\n");
return NULL;
}
/* Compute initial condition value */
val = gen_expr->eval_gen_expr(-1,-1);
/* Free the general expression now that we are done with it */
delete gen_expr;
/* integer value (boolean is an integer in C) */
if (param->type == P_TYPE_BOOL)
{
init_val.bool_val = (bool)val;
}
else if ((param->type == P_TYPE_INT))
{
init_val.int_val = (int)val;
}
/* float value */
else if (param->type == P_TYPE_DOUBLE)
{
init_val.float_val = val;
}
/* Unknown value */
else
{
if (PARSE_DEBUG) printf("pase_per_frame_init_eqn: unknown parameter type!\n");
return NULL;
}
/* Create new initial condition */
if ((init_cond = new InitCond(param, init_val)) == NULL)
{
if (PARSE_DEBUG) printf("parse_per_frame_init_eqn: new_init_cond failed!\n");
return NULL;
}
init_cond->evaluate(true);
/* Finished */
return init_cond;
}
bool
Parser::scanForComment(std::istream & fs)
{
int c;
c = fs.get();
if (c == '/') {
while (true)
{
if (!fs || fs.eof())
return true;
else
c = fs.get();
if (c == EOF)
return true;
if (c == '\n')
{
return true;
}
}
} else {
fs.unget();
return false;
}
}
void
Parser::readStringUntil(std::istream & fs, std::string * out_buffer, bool wrapAround, const std::set<char> & skipList)
{
int string_line_buffer_index = 0;
int c;
/* Loop until a delimiter is found, or the maximum string size is found */
while (true)
{
if (!fs || fs.eof())
c = EOF;
else
c = fs.get();
/* Now interpret the character */
switch (c)
{
case '/':
{
bool commentExisted = scanForComment(fs);
if (!commentExisted) {
out_buffer->push_back(c);
break;
} else {
line_count++;
return;
}
}
case '\n':
if (!out_buffer->empty() && ((*out_buffer)[out_buffer->length() -1] == '\n'))
return;
line_count++;
if (wrapAround)
{
std::ostringstream buffer;
// if (PARSE_DEBUG) std::cerr << "token wrap! line " << line_count << std::endl;
while (c != '=')
{
if (!fs || fs.eof())
{
line_count = 1;
line_mode = UNSET_LINE_MODE;
// if (PARSE_DEBUG) std::cerr << "token wrap: end of file" << std::endl;
return;
}
else {
c = fs.get();
if ( c != '=')
buffer << c;
}
}
if (!wrapsToNextLine(buffer.str())) {
wrapAround = false;
int buf_size = (int)buffer.str().length();
// <= to also remove equal sign parsing from stream
for (int k = 0; k <= buf_size; k++) {
if (fs)
fs.unget();
else
abort();
}
return;
}
break;
} else
out_buffer->push_back(c);
return;
case EOF:
line_count = 1;
return;
default:
if (out_buffer != NULL)
{
if (skipList.find(c) == skipList.end())
out_buffer->push_back(c);
}
}
}
}
int
Parser::parse_wavecode(char * token, std::istream & fs, MilkdropPreset * preset)
{
char * var_string;
InitCond * init_cond;
CustomWave * custom_wave;
int id;
CValue init_val;
Param * param;
assert(preset);
assert(fs);
assert(token);
/* token should be in the form wavecode_N_var, such as wavecode_1_samples */
/* Get id and variable name from token string */
if (parse_wavecode_prefix(token, &id, &var_string) < 0)
return PROJECTM_PARSE_ERROR;
last_custom_wave_id = id;
if (PARSE_DEBUG) printf("parse_wavecode: wavecode id = %d, parameter = \"%s\"\n", id, var_string);
/* Retrieve custom wave information from preset, allocating new one if necessary */
if ((custom_wave = MilkdropPreset::find_custom_object(id, preset->customWaves)) == NULL)
{
std::cerr << "parse_wavecode: failed to load (or create) custom wave (id = "
<< id << ")!\n"
<< std::endl;
return PROJECTM_FAILURE;
}
if (PARSE_DEBUG) printf("parse_wavecode: custom wave found (id = %d)\n", custom_wave->id);
/* Retrieve parameter from this custom waves parameter db */
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(var_string,&custom_wave->param_tree)) == NULL)
return PROJECTM_FAILURE;
if (PARSE_DEBUG) printf("parse_wavecode: custom wave parameter found (name = %s)\n", param->name.c_str());
/* integer value (boolean is an integer in C) */
if ((param->type == P_TYPE_BOOL))
{
int bool_test;
if ((parse_int(fs, &bool_test)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_wavecode: error parsing integer!\n");
return PROJECTM_PARSE_ERROR;
}
init_val.bool_val = bool_test;
}
else if ((param->type == P_TYPE_INT))
{
if ((parse_int(fs, (int*)&init_val.int_val)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_wavecode: error parsing integer!\n");
return PROJECTM_PARSE_ERROR;
}
}
/* float value */
else if (param->type == P_TYPE_DOUBLE)
{
if ((parse_float(fs, (float*)&init_val.float_val)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_wavecode: error parsing float!\n");
return PROJECTM_PARSE_ERROR;
}
}
/* Unknown value */
else
{
if (PARSE_DEBUG) printf("parse_wavecode: unknown parameter type!\n");
return PROJECTM_PARSE_ERROR;
}
/* Create new initial condition */
init_cond = new InitCond(param, init_val);
if (init_cond == NULL)
{
if (PARSE_DEBUG) printf("parse_wavecode: new_init_cond failed!\n");
return PROJECTM_FAILURE;
}
std::pair<std::map<std::string, InitCond*>::iterator, bool> inserteePair =
custom_wave->init_cond_tree.insert(std::make_pair(init_cond->param->name, init_cond));
// assert(inserteePair.second);
line_mode = CUSTOM_WAVE_WAVECODE_LINE_MODE;
if (PARSE_DEBUG) printf("parse_wavecode: [success]\n");
return PROJECTM_SUCCESS;
}
int
Parser::parse_shapecode(char * token, std::istream & fs, MilkdropPreset * preset)
{
char * var_string;
InitCond * init_cond;
CustomShape * custom_shape;
int id;
CValue init_val;
Param * param;
/* Null argument checks */
if (preset == NULL)
return PROJECTM_FAILURE;
if (fs == NULL)
return PROJECTM_FAILURE;
if (token == NULL)
return PROJECTM_FAILURE;
/* token should be in the form shapecode_N_var, such as shapecode_1_samples */
/* Get id and variable name from token string */
if (parse_shapecode_prefix(token, &id, &var_string) < 0)
return PROJECTM_PARSE_ERROR;
last_custom_shape_id = id;
if (PARSE_DEBUG) printf("parse_shapecode: shapecode id = %d, parameter = \"%s\"\n", id, var_string);
/* Retrieve custom shape information from preset. The 3rd argument
if true creates a custom shape if one does not exist */
if ((custom_shape = MilkdropPreset::find_custom_object(id, preset->customShapes)) == NULL)
{
if (PARSE_DEBUG) printf("parse_shapecode: failed to load (or create) custom shape (id = %d)!\n", id);
return PROJECTM_FAILURE;
}
if (PARSE_DEBUG) printf("parse_shapecode: custom shape found (id = %d)\n", custom_shape->id);
if ((param = ParamUtils::find<ParamUtils::NO_CREATE>(var_string, &custom_shape->text_properties_tree)) != NULL)
{
std::string text;//[MAX_TOKEN_SIZE];
//token_t token = parseToken(fs, text);
fs >> text;
*((std::string*)param->engine_val) = text;
if (PARSE_DEBUG)
std::cerr << "parse_shapecode: found image url, text is \""
<< text << "\""
<< std::endl;
return PROJECTM_SUCCESS;
}
/* Retrieve parameter from this custom shapes parameter db */
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(var_string, &custom_shape->param_tree)) == NULL)
{
if (PARSE_DEBUG) printf("parse_shapecode: failed to create parameter.\n");
return PROJECTM_FAILURE;
}
if (PARSE_DEBUG) printf("parse_shapecode: custom shape parameter found (name = %s)\n", param->name.c_str());
/* integer value (boolean is an integer in C) */
if ((param->type == P_TYPE_BOOL))
{
int bool_test;
if ((parse_int(fs, &bool_test)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_shapecode: error parsing integer!\n");
return PROJECTM_PARSE_ERROR;
}
init_val.bool_val = bool_test;
}
else if ((param->type == P_TYPE_INT))
{
if ((parse_int(fs, (int*)&init_val.int_val)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_shapecode: error parsing integer!\n");
return PROJECTM_PARSE_ERROR;
}
}
/* float value */
else if (param->type == P_TYPE_DOUBLE)
{
if ((parse_float(fs, (float*)&init_val.float_val)) == PROJECTM_PARSE_ERROR)
{
if (PARSE_DEBUG) printf("parse_shapecode: error parsing float!\n");
return PROJECTM_PARSE_ERROR;
}
}
/* Unknown value */
else
{
if (PARSE_DEBUG) printf("parse_shapecode: unknown parameter type!\n");
return PROJECTM_PARSE_ERROR;
}
/* Create new initial condition */
if ((init_cond = new InitCond(param, init_val)) == NULL)
{
if (PARSE_DEBUG) printf("parse_shapecode: new_init_cond failed!\n");
return PROJECTM_FAILURE;
}
custom_shape->init_cond_tree.insert(std::make_pair(param->name,init_cond));
line_mode = CUSTOM_SHAPE_SHAPECODE_LINE_MODE;
if (PARSE_DEBUG) printf("parse_shapecode: [success]\n");
return PROJECTM_SUCCESS;
}
int
Parser::parse_wavecode_prefix(char * token, int * id, char ** var_string)
{
int len, i, j;
if (token == NULL)
return PROJECTM_FAILURE;
/*
if (*var_string == NULL)
return PROJECTM_FAILURE;
*/
if (id == NULL)
return PROJECTM_FAILURE;
len = strlen(token);
/* Move pointer passed "wavecode_" prefix */
if (len <= WAVECODE_STRING_LENGTH)
return PROJECTM_FAILURE;
i = WAVECODE_STRING_LENGTH;
j = 0;
(*id) = 0;
/* This loop grabs the integer id for this custom wave */
while ((i < len) && (token[i] >= 48) && (token[i] <= 57))
{
if (j >= MAX_TOKEN_SIZE)
return PROJECTM_FAILURE;
(*id) = 10*(*id) + (token[i]-48);
j++;
i++;
}
if (i > (len - 2))
return PROJECTM_FAILURE;
*var_string = token + i + 1;
return PROJECTM_SUCCESS;
}
int
Parser::parse_shapecode_prefix(char * token, int * id, char ** var_string)
{
int len, i, j;
if (token == NULL)
return PROJECTM_FAILURE;
/*
if (*var_string == NULL)
return PROJECTM_FAILURE;
*/
if (id == NULL)
return PROJECTM_FAILURE;
len = strlen(token);
/* Move pointer passed "shapecode_" prefix */
if (len <= SHAPECODE_STRING_LENGTH)
return PROJECTM_FAILURE;
i = SHAPECODE_STRING_LENGTH;
j = 0;
(*id) = 0;
/* This loop grabs the integer id for this custom shape */
while ((i < len) && (token[i] >= 48) && (token[i] <= 57))
{
if (j >= MAX_TOKEN_SIZE)
return PROJECTM_FAILURE;
(*id) = 10*(*id) + (token[i]-48);
j++;
i++;
}
if (i > (len - 2))
return PROJECTM_FAILURE;
*var_string = token + i + 1;
return PROJECTM_SUCCESS;
}
int
Parser::parse_wave_prefix(char * token, int * id, char ** eqn_string)
{
int len, i, j;
if (token == NULL)
return PROJECTM_FAILURE;
if (eqn_string == NULL)
return PROJECTM_FAILURE;
if (id == NULL)
return PROJECTM_FAILURE;
len = strlen(token);
if (len <= WAVE_STRING_LENGTH)
return PROJECTM_FAILURE;
i = WAVE_STRING_LENGTH;
j = 0;
(*id) = 0;
/* This loop grabs the integer id for this custom wave */
while ((i < len) && (token[i] >= 48) && (token[i] <= 57))
{
if (j >= MAX_TOKEN_SIZE)
return PROJECTM_FAILURE;
(*id) = 10*(*id) + (token[i]-48);
j++;
i++;
}
if (i > (len - 2))
return PROJECTM_FAILURE;
*eqn_string = token + i + 1;
if (PARSE_DEBUG) printf("parse_wave_prefix: prefix = %s\n (LINE %d)", *eqn_string, line_count);
return PROJECTM_SUCCESS;
}
int
Parser::parse_shape_prefix(char * token, int * id, char ** eqn_string)
{
int len, i, j;
if (token == NULL)
return PROJECTM_FAILURE;
if (eqn_string == NULL)
return PROJECTM_FAILURE;
if (id == NULL)
return PROJECTM_FAILURE;
len = strlen(token);
if (len <= SHAPE_STRING_LENGTH)
return PROJECTM_FAILURE;
i = SHAPE_STRING_LENGTH;
j = 0;
(*id) = 0;
/* This loop grabs the integer id for this custom wave */
while ((i < len) && (token[i] >= 48) && (token[i] <= 57))
{
if (j >= MAX_TOKEN_SIZE)
return PROJECTM_FAILURE;
(*id) = 10*(*id) + (token[i]-48);
j++;
i++;
}
if (i > (len - 2))
return PROJECTM_FAILURE;
*eqn_string = token + i + 1;
return PROJECTM_SUCCESS;
}
/* Parses custom wave equations */
int
Parser::parse_wave(char * token, std::istream & fs, MilkdropPreset * preset)
{
int id;
char * eqn_type;
if (PARSE_DEBUG) printf("parse_wave:begin\n");
if (token == NULL)
return PROJECTM_FAILURE;
if (fs == NULL)
return PROJECTM_FAILURE;
if (preset == NULL)
return PROJECTM_FAILURE;
/* Grab custom wave id and equation type (per frame or per point) from string token */
if (parse_wave_prefix(token, &id, &eqn_type) < 0)
{
if (PARSE_DEBUG) printf("parse_wave: syntax error in custom wave prefix!\n");
return PROJECTM_FAILURE;
}
strncpy(last_eqn_type, eqn_type, MAX_TOKEN_SIZE);
return parse_wave_helper(fs, preset, id, eqn_type, 0);
}
int
Parser::parse_wave_helper(std::istream & fs, MilkdropPreset * preset, int id, char * eqn_type, char * init_string)
{
Param * param;
GenExpr * gen_expr;
char string[MAX_TOKEN_SIZE];
PerFrameEqn * per_frame_eqn;
CustomWave * custom_wave;
InitCond * init_cond;
/* Retrieve custom wave associated with this id */
if ((custom_wave = MilkdropPreset::find_custom_object(id, preset->customWaves)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave_helper: custom wave id %d not found!\n", id);
return PROJECTM_FAILURE;
}
/* per frame init equation case */
if (!strncmp(eqn_type, WAVE_INIT_STRING, WAVE_INIT_STRING_LENGTH))
{
if (PARSE_DEBUG) printf("parse_wave_helper (per frame init): [begin] (LINE %d)\n", line_count);
/* Parse the per frame init equation */
if ((init_cond = parse_per_frame_init_eqn(fs, preset, &custom_wave->param_tree)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave_helper (per frame init): equation parsing failed (LINE %d)\n", line_count);
return PROJECTM_PARSE_ERROR;
}
/* Insert the equation in the per frame equation tree */
custom_wave->per_frame_init_eqn_tree.insert(std::make_pair(init_cond->param->name,init_cond));
line_mode = CUSTOM_WAVE_PER_FRAME_INIT_LINE_MODE;
init_cond->evaluate(true);
return PROJECTM_SUCCESS;
}
/* per frame equation case */
if (!strncmp(eqn_type, PER_FRAME_STRING_NO_UNDERSCORE, PER_FRAME_STRING_NO_UNDERSCORE_LENGTH))
{
if (PARSE_DEBUG) printf("parse_wave_helper (per_frame): [start] (custom wave id = %d)\n", custom_wave->id);
if (parseToken(fs, string) != tEq)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): no equal sign after string \"%s\" (LINE %d)\n", string, line_count);
return PROJECTM_PARSE_ERROR;
}
/* Find the parameter associated with the string in the custom wave database */
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(string, &custom_wave->param_tree)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): parameter \"%s\" not found or cannot be wipemalloc'ed!!\n", string);
return PROJECTM_FAILURE;
}
/* Make sure parameter is writable */
if (param->flags & P_FLAG_READONLY)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): parameter %s is marked as read only (LINE %d)\n", param->name.c_str(), line_count);
return PROJECTM_FAILURE;
}
/* Parse right side of equation as an expression */
current_wave = custom_wave;
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): equation evaluated to null (LINE %d)\n", line_count);
current_wave = NULL;
return PROJECTM_PARSE_ERROR;
}
current_wave = NULL;
if (PARSE_DEBUG) printf("parse_wave (per_frame): [finished parsing equation] (LINE %d)\n", line_count);
/* Create a new per frame equation */
if ((per_frame_eqn = new PerFrameEqn(custom_wave->per_frame_count++, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
return PROJECTM_FAILURE;
}
custom_wave->per_frame_eqn_tree.push_back(per_frame_eqn);
if (PARSE_DEBUG) printf("parse_wave (per_frame): equation %d associated with custom wave %d [success]\n",
per_frame_eqn->index, custom_wave->id);
/* Need to add stuff to string buffer so the editor can read the equations.
Why not make a nice little helper function for this? - here it is: */
line_mode = CUSTOM_WAVE_PER_FRAME_LINE_MODE;
return PROJECTM_SUCCESS;
}
/* per point equation case */
if (!strncmp(eqn_type, PER_POINT_STRING, PER_POINT_STRING_LENGTH))
{
if (PARSE_DEBUG) printf("parse_wave_helper (per_point): per_pixel equation parsing start...(LINE %d)\n", line_count);
/// HACK the parse_line code already parsed the per_pixel variable name. This handles that case
/// Parser needs reworked. Don't have time for it. So this is the result.
if (init_string)
strncpy(string, init_string, strlen(init_string)+1);
else
{
if (parseToken(fs, string) != tEq)
{ /* parse per pixel operator name */
if (PARSE_DEBUG) printf("parse_wave_helper (per_point): equal operator missing after per pixel operator. Last token = \"%s\" (LINE %d)\n", string, line_count);
return PROJECTM_PARSE_ERROR;
}
}
/* Parse right side of equation as an expression, First tell parser we are parsing a custom wave */
current_wave = custom_wave;
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave_helper (per_point): equation evaluated to null? (LINE %d)\n", line_count);
return PROJECTM_PARSE_ERROR;
}
/* Add the per point equation */
if (custom_wave->add_per_point_eqn(string, gen_expr) < 0)
{
delete gen_expr;
return PROJECTM_PARSE_ERROR;
}
// This tells the parser we are no longer parsing a custom wave
current_wave = NULL;
line_mode = CUSTOM_WAVE_PER_POINT_LINE_MODE;
if (PARSE_DEBUG) printf("parse_wave_helper (per_point): [finished] (custom wave id = %d)\n", custom_wave->id);
return PROJECTM_SUCCESS;
}
return PROJECTM_FAILURE;
}
/* Parses custom shape equations */
int
Parser::parse_shape(char * token, std::istream & fs, MilkdropPreset * preset)
{
int id;
char * eqn_type;
CustomShape * custom_shape;
if (token == NULL)
return PROJECTM_FAILURE;
if (fs == NULL)
return PROJECTM_FAILURE;
if (preset == NULL)
return PROJECTM_FAILURE;
/* Grab custom shape id and equation type (per frame or per point) from string token */
if (parse_shape_prefix(token, &id, &eqn_type) < 0)
{
if (PARSE_DEBUG) printf("parse_shape: syntax error in custom shape prefix!\n");
return PROJECTM_PARSE_ERROR;
}
/* Retrieve custom shape associated with this id */
if ((custom_shape = MilkdropPreset::find_custom_object(id,preset->customShapes)) == NULL)
return PROJECTM_FAILURE;
/* per frame init equation case */
if (!strncmp(eqn_type, SHAPE_INIT_STRING, SHAPE_INIT_STRING_LENGTH))
{
return parse_shape_per_frame_init_eqn(fs, custom_shape, preset);
}
/* per frame equation case */
if (!strncmp(eqn_type, PER_FRAME_STRING_NO_UNDERSCORE, PER_FRAME_STRING_NO_UNDERSCORE_LENGTH))
{
return parse_shape_per_frame_eqn(fs, custom_shape, preset);
}
/* Syntax error, return parse error */
return PROJECTM_PARSE_ERROR;
}
/* Helper function to update the string buffers used by the editor */
/* Helper function: returns the length of the prefix portion in the line
buffer (the passed string here). In other words, given
the string 'per_frame_1 = x = ....', return the length of 'per_frame_1 = '
Returns -1 if syntax error
*/
int
Parser::get_string_prefix_len(char * string)
{
int i = 0;
/* Null argument check */
if (string == NULL)
return PROJECTM_FAILURE;
/* First find the equal sign */
while (string[i] != '=')
{
if (string[i] == 0)
return PROJECTM_FAILURE;
i++;
}
/* If the string already ends at the next char then give up */
if (string[i+1] == 0)
return PROJECTM_FAILURE;
/* Move past the equal sign */
i++;
/* Now found the start of the LHS variable, ie skip the spaces */
while(string[i] == ' ')
{
i++;
}
/* If this is the end of the string then its a syntax error */
if (string[i] == 0)
return PROJECTM_FAILURE;
/* Finished successfully, return the length */
return i;
}
int
Parser::parse_shape_per_frame_init_eqn(std::istream & fs, CustomShape * custom_shape, MilkdropPreset * preset)
{
InitCond * init_cond;
if (PARSE_DEBUG) printf("parse_shape (per frame init): [begin] (LINE %d)\n", line_count);
/* Parse the per frame equation */
if ((init_cond = parse_per_frame_init_eqn(fs, preset, &custom_shape->param_tree)) == NULL)
{
if (PARSE_DEBUG) printf("parse_shape (per frame init): equation parsing failed (LINE %d)\n", line_count);
return PROJECTM_PARSE_ERROR;
}
/// \idea possibly a good place to update a string buffer;
line_mode = CUSTOM_SHAPE_PER_FRAME_INIT_LINE_MODE;
init_cond->evaluate(true);
return PROJECTM_SUCCESS;
}
int
Parser::parse_shape_per_frame_eqn(std::istream & fs, CustomShape * custom_shape, MilkdropPreset * preset)
{
Param * param;
GenExpr * gen_expr;
PerFrameEqn * per_frame_eqn;
char string[MAX_TOKEN_SIZE];
if (PARSE_DEBUG) printf("parse_shape (per_frame): [start] (custom shape id = %d)\n", custom_shape->id);
if (parseToken(fs, string) != tEq)
{
if (PARSE_DEBUG) printf("parse_shape (per_frame): no equal sign after string \"%s\" (LINE %d)\n", string, line_count);
return PROJECTM_PARSE_ERROR;
}
/* Find the parameter associated with the string in the custom shape database */
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(string, &custom_shape->param_tree)) == NULL)
{
if (PARSE_DEBUG) printf("parse_shape (per_frame): parameter \"%s\" not found or cannot be wipemalloc'ed!!\n", string);
return PROJECTM_FAILURE;
}
/* Make sure parameter is writable */
if (param->flags & P_FLAG_READONLY)
{
if (PARSE_DEBUG) printf("parse_shape (per_frame): parameter %s is marked as read only (LINE %d)\n", param->name.c_str(), line_count);
return PROJECTM_PARSE_ERROR;
}
/* Parse right side of equation as an expression */
current_shape = custom_shape;
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_shape (per_frame): equation evaluated to null (LINE %d)\n", line_count);
current_shape = NULL;
return PROJECTM_PARSE_ERROR;
}
current_shape = NULL;
if (PARSE_DEBUG) printf("parse_shape (per_frame): [finished parsing equation] (LINE %d)\n", line_count);
/* Create a new per frame equation */
if ((per_frame_eqn = new PerFrameEqn(custom_shape->per_frame_count++, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_shape (per_frame): failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
return PROJECTM_FAILURE;
}
custom_shape->per_frame_eqn_tree.push_back(per_frame_eqn);
/// \idea add string buffer update for easy >> and <<
line_mode = CUSTOM_SHAPE_PER_FRAME_LINE_MODE;
return PROJECTM_SUCCESS;
}
int
Parser::parse_wave_per_frame_eqn(std::istream & fs, CustomWave * custom_wave, MilkdropPreset * preset)
{
Param * param;
GenExpr * gen_expr;
PerFrameEqn * per_frame_eqn;
char string[MAX_TOKEN_SIZE];
if (PARSE_DEBUG) printf("parse_wave (per_frame): [start] (custom shape id = %d)\n", custom_wave->id);
if (parseToken(fs, string) != tEq)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): no equal sign after string \"%s\" (LINE %d)\n", string, line_count);
return PROJECTM_PARSE_ERROR;
}
/* Find the parameter associated with the string in the custom shape database */
if ((param = ParamUtils::find<ParamUtils::AUTO_CREATE>(string, &custom_wave->param_tree)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): parameter \"%s\" not found or cannot be wipemalloc'ed!!\n", string);
return PROJECTM_FAILURE;
}
/* Make sure parameter is writable */
if (param->flags & P_FLAG_READONLY)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): parameter %s is marked as read only (LINE %d)\n", param->name.c_str(), line_count);
return PROJECTM_FAILURE;
}
/* Parse right side of equation as an expression */
current_wave = custom_wave;
if ((gen_expr = parse_gen_expr(fs, NULL, preset)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): equation evaluated to null (LINE %d)\n", line_count);
current_wave = NULL;
return PROJECTM_PARSE_ERROR;
}
current_wave = NULL;
if (PARSE_DEBUG) printf("parse_wave (per_frame): [finished parsing equation] (LINE %d)\n", line_count);
/* Create a new per frame equation */
if ((per_frame_eqn = new PerFrameEqn(custom_wave->per_frame_count++, param, gen_expr)) == NULL)
{
if (PARSE_DEBUG) printf("parse_wave (per_frame): failed to create a new per frame eqn, out of memory?\n");
delete gen_expr;
return PROJECTM_FAILURE;
}
custom_wave->per_frame_eqn_tree.push_back(per_frame_eqn);
if (PARSE_DEBUG) printf("parse_wave (per_frame): equation %d associated with custom wave %d [success]\n",
per_frame_eqn->index, custom_wave->id);
/* Need to add stuff to string buffer so the editor can read the equations.
Why not make a nice little helper function for this? - here it is: */
line_mode = CUSTOM_WAVE_PER_FRAME_LINE_MODE;
return PROJECTM_SUCCESS;
}
bool
Parser::wrapsToNextLine(const std::string & str)
{
std::size_t lastLineEndIndex = lastLinePrefix.find_last_not_of("0123456789");
std::size_t thisLineEndIndex = str.find_last_not_of("0123456789");
std::size_t startIndex = 0;
if ((str.compare(startIndex, lastLineEndIndex, lastLinePrefix.c_str(), thisLineEndIndex)) == 0)
return true;
else
return false;
}