| /** |
| * 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), ¤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) |
| { |
| 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; |
| } |