2554 lines
70 KiB
C++
2554 lines
70 KiB
C++
/**
|
|
* 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)
|
|
{
|
|
|
|
char 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 << 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:
|
|
|
|
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)
|
|
{
|
|
memcpy(string, init_string, strlen(init_string) + 1);
|
|
}
|
|
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), ¤t_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), ¤t_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), ¤t_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), ¤t_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)
|
|
{
|
|
std::istringstream stream(string);
|
|
stream >> *float_ptr;
|
|
|
|
if (stream.fail())
|
|
return PROJECTM_PARSE_ERROR;
|
|
|
|
return PROJECTM_SUCCESS;
|
|
}
|
|
|
|
/* Parses a floating point number */
|
|
int Parser::parse_float(std::istream & fs, float * float_ptr)
|
|
{
|
|
|
|
char string[MAX_TOKEN_SIZE];
|
|
token_t token;
|
|
int sign;
|
|
|
|
|
|
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)
|
|
{
|
|
return PROJECTM_PARSE_ERROR;
|
|
}
|
|
|
|
std::istringstream stream(string);
|
|
stream >> *float_ptr;
|
|
|
|
return PROJECTM_SUCCESS;
|
|
}
|
|
|
|
/* 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 (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 ((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) {
|
|
|
|
char 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;
|
|
char c;
|
|
char p;
|
|
/* 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 (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 (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)
|
|
memcpy(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 (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 succesfully, 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;
|
|
|
|
}
|