#line 2 "grammar.epilogue"

/*************************************************************************
 * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com>               *
 *                                                                       *
 * This file is part of Dimension.                                       *
 *                                                                       *
 * Dimension is free software; you can redistribute it and/or modify it  *
 * under the terms of the GNU General Public License as published by the *
 * Free Software Foundation; either version 3 of the License, or (at     *
 * your option) any later version.                                       *
 *                                                                       *
 * Dimension is distributed in the hope that it will be useful, but      *
 * WITHOUT ANY WARRANTY; without even the implied warranty of            *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 * General Public License for more details.                              *
 *                                                                       *
 * You should have received a copy of the GNU General Public License     *
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 *************************************************************************/

dmnsn_astree *
dmnsn_parse(FILE *file, dmnsn_symbol_table *symtable)
{
  const char *filename;
  dmnsn_astnode *fnode = dmnsn_find_symbol(symtable, "$file");
  if (fnode && fnode->type == DMNSN_AST_STRING) {
    filename = fnode->ptr;
  } else {
    filename = "<>";
    dmnsn_declare_symbol(symtable, "$file", dmnsn_new_ast_string(filename));
  }

  void *scanner;
  dmnsn_astree *astree = dmnsn_new_array(sizeof(dmnsn_astnode));

  dmnsn_yylex_init(&scanner);
  dmnsn_yyset_in(file, scanner);
  dmnsn_yyset_extra(NULL, scanner);

  if (yyparse(filename, scanner, astree, symtable) != 0) {
    dmnsn_delete_astree(astree);
    astree = NULL;
  }

  dmnsn_yylex_cleanup(scanner);
  dmnsn_yylex_destroy(scanner);
  return astree;
}

dmnsn_astree *
dmnsn_parse_string(const char *str, dmnsn_symbol_table *symtable)
{
  const char *filename;
  dmnsn_astnode *fnode = dmnsn_find_symbol(symtable, "$file");
  if (fnode && fnode->type == DMNSN_AST_STRING) {
    filename = fnode->ptr;
  } else {
    filename = "<string>";
    dmnsn_declare_symbol(symtable, "$file", dmnsn_new_ast_string(filename));
  }

  void *scanner;
  dmnsn_astree *astree = dmnsn_new_array(sizeof(dmnsn_astnode));

  dmnsn_yylex_init(&scanner);
  dmnsn_yyset_extra(NULL, scanner);

  void *buffer = dmnsn_yy_make_string_buffer(str, scanner);
  dmnsn_yy_push_buffer(buffer, scanner);

  if (yyparse(filename, scanner, astree, symtable) != 0) {
    dmnsn_delete_astree(astree);
    astree = NULL;
  }

  dmnsn_yylex_cleanup(scanner);
  dmnsn_yylex_destroy(scanner);
  return astree;
}

const char *
dmnsn_token_string(dmnsn_token_type token_type)
{
#define TOKEN_SIZE 255
  static char token[TOKEN_SIZE + 1];

  unsigned int i = YYTRANSLATE(token_type);
  if (i > YYNTOKENS) {
    fprintf(stderr, "WARNING: unrecognised token %d.\n", (int)token_type);
    return "unrecognized-token";
  }

  /* Trim the quotation marks */

  if (strlen(yytname[i]) - 1 >= TOKEN_SIZE) {
    fprintf(stderr, "WARNING: name of token %d too long.\n", (int)token_type);
    return "unrepresentable-token";
  }

  strcpy(token, yytname[i] + 1);
  token[strlen(token) - 1] = '\0';

  return token;
#undef TOKEN_SIZE
}

const char *
dmnsn_astnode_string(dmnsn_astnode_type astnode_type)
{
  switch (astnode_type) {
  /* Macro to shorten this switch */
#define dmnsn_astnode_map(type, str)                                           \
  case type:                                                                   \
    return str;

  dmnsn_astnode_map(DMNSN_AST_NONE, "none");

  dmnsn_astnode_map(DMNSN_AST_GLOBAL_SETTINGS, "global_settings");
  dmnsn_astnode_map(DMNSN_AST_ASSUMED_GAMMA,   "assumed_gamma");
  dmnsn_astnode_map(DMNSN_AST_MAX_TRACE_LEVEL, "max_trace_level");

  dmnsn_astnode_map(DMNSN_AST_BACKGROUND, "background");

  dmnsn_astnode_map(DMNSN_AST_CAMERA,      "camera");
  dmnsn_astnode_map(DMNSN_AST_PERSPECTIVE, "perspective");
  dmnsn_astnode_map(DMNSN_AST_LOCATION,    "location");
  dmnsn_astnode_map(DMNSN_AST_RIGHT,       "right");
  dmnsn_astnode_map(DMNSN_AST_UP,          "up");
  dmnsn_astnode_map(DMNSN_AST_SKY,         "sky");
  dmnsn_astnode_map(DMNSN_AST_ANGLE,       "angle");
  dmnsn_astnode_map(DMNSN_AST_LOOK_AT,     "look_at");
  dmnsn_astnode_map(DMNSN_AST_DIRECTION,   "direction");

  dmnsn_astnode_map(DMNSN_AST_OBJECT,       "object");
  dmnsn_astnode_map(DMNSN_AST_BOX,          "box");
  dmnsn_astnode_map(DMNSN_AST_DIFFERENCE,   "difference");
  dmnsn_astnode_map(DMNSN_AST_INTERSECTION, "intersection");
  dmnsn_astnode_map(DMNSN_AST_LIGHT_SOURCE, "light_source");
  dmnsn_astnode_map(DMNSN_AST_MERGE,        "merge");
  dmnsn_astnode_map(DMNSN_AST_PLANE,        "plane");
  dmnsn_astnode_map(DMNSN_AST_SPHERE,       "sphere");
  dmnsn_astnode_map(DMNSN_AST_UNION,        "union");

  dmnsn_astnode_map(DMNSN_AST_OBJECT_MODIFIERS, "object-modifiers");

  dmnsn_astnode_map(DMNSN_AST_TEXTURE, "texture");

  dmnsn_astnode_map(DMNSN_AST_PIGMENT,           "pigment");
  dmnsn_astnode_map(DMNSN_AST_PIGMENT_MODIFIERS, "pigment-modifiers");
  dmnsn_astnode_map(DMNSN_AST_IMAGE_MAP,         "image_map");
  dmnsn_astnode_map(DMNSN_AST_PNG,               "png");

  dmnsn_astnode_map(DMNSN_AST_FINISH,     "finish");
  dmnsn_astnode_map(DMNSN_AST_AMBIENT,    "ambient");
  dmnsn_astnode_map(DMNSN_AST_DIFFUSE,    "diffuse");
  dmnsn_astnode_map(DMNSN_AST_PHONG,      "phong");
  dmnsn_astnode_map(DMNSN_AST_PHONG_SIZE, "phong_size");

  dmnsn_astnode_map(DMNSN_AST_REFLECTION,       "reflection");
  dmnsn_astnode_map(DMNSN_AST_REFLECTION_ITEMS, "reflection-items");
  dmnsn_astnode_map(DMNSN_AST_FALLOFF,          "falloff");

  dmnsn_astnode_map(DMNSN_AST_INTERIOR, "interior");
  dmnsn_astnode_map(DMNSN_AST_IOR,      "ior");

  dmnsn_astnode_map(DMNSN_AST_TRANSFORMATION, "transformation");
  dmnsn_astnode_map(DMNSN_AST_ROTATION,       "rotation");
  dmnsn_astnode_map(DMNSN_AST_SCALE,          "scale");
  dmnsn_astnode_map(DMNSN_AST_TRANSLATION,    "translation");
  dmnsn_astnode_map(DMNSN_AST_MATRIX,         "matrix");
  dmnsn_astnode_map(DMNSN_AST_INVERSE,        "inverse");

  dmnsn_astnode_map(DMNSN_AST_FLOAT, "float");
  dmnsn_astnode_map(DMNSN_AST_INTEGER, "integer");

  dmnsn_astnode_map(DMNSN_AST_VECTOR, "vector");

  dmnsn_astnode_map(DMNSN_AST_ADD, "+");
  dmnsn_astnode_map(DMNSN_AST_SUB, "-");
  dmnsn_astnode_map(DMNSN_AST_MUL, "*");
  dmnsn_astnode_map(DMNSN_AST_DIV, "/");

  dmnsn_astnode_map(DMNSN_AST_EQUAL,         "=" );
  dmnsn_astnode_map(DMNSN_AST_NOT_EQUAL,     "!=");
  dmnsn_astnode_map(DMNSN_AST_LESS,          "<" );
  dmnsn_astnode_map(DMNSN_AST_LESS_EQUAL,    "<=");
  dmnsn_astnode_map(DMNSN_AST_GREATER,       ">" );
  dmnsn_astnode_map(DMNSN_AST_GREATER_EQUAL, ">=");
  dmnsn_astnode_map(DMNSN_AST_AND,           "&" );
  dmnsn_astnode_map(DMNSN_AST_OR,            "|" );
  dmnsn_astnode_map(DMNSN_AST_NOT,           "!" );
  dmnsn_astnode_map(DMNSN_AST_TERNARY,       "?:");

  dmnsn_astnode_map(DMNSN_AST_ABS,          "abs");
  dmnsn_astnode_map(DMNSN_AST_ACOS,         "acos");
  dmnsn_astnode_map(DMNSN_AST_ACOSH,        "acosh");
  dmnsn_astnode_map(DMNSN_AST_ASC,          "asc");
  dmnsn_astnode_map(DMNSN_AST_ASIN,         "asin");
  dmnsn_astnode_map(DMNSN_AST_ASINH,        "asinh");
  dmnsn_astnode_map(DMNSN_AST_ATAN,         "atan");
  dmnsn_astnode_map(DMNSN_AST_ATAN2,        "atan2");
  dmnsn_astnode_map(DMNSN_AST_ATANH,        "atanh");
  dmnsn_astnode_map(DMNSN_AST_CEIL,         "ceil");
  dmnsn_astnode_map(DMNSN_AST_COS,          "cos");
  dmnsn_astnode_map(DMNSN_AST_COSH,         "cosh");
  dmnsn_astnode_map(DMNSN_AST_DEGREES,      "degrees");
  dmnsn_astnode_map(DMNSN_AST_INT_DIV,      "div");
  dmnsn_astnode_map(DMNSN_AST_EXP,          "exp");
  dmnsn_astnode_map(DMNSN_AST_FLOOR,        "floor");
  dmnsn_astnode_map(DMNSN_AST_INT,          "int");
  dmnsn_astnode_map(DMNSN_AST_LN,           "ln");
  dmnsn_astnode_map(DMNSN_AST_LOG,          "log");
  dmnsn_astnode_map(DMNSN_AST_MAX,          "max");
  dmnsn_astnode_map(DMNSN_AST_MIN,          "min");
  dmnsn_astnode_map(DMNSN_AST_MOD,          "mod");
  dmnsn_astnode_map(DMNSN_AST_POW,          "pow");
  dmnsn_astnode_map(DMNSN_AST_RADIANS,      "radians");
  dmnsn_astnode_map(DMNSN_AST_SIN,          "sin");
  dmnsn_astnode_map(DMNSN_AST_SINH,         "sinh");
  dmnsn_astnode_map(DMNSN_AST_SQRT,         "sqrt");
  dmnsn_astnode_map(DMNSN_AST_STRCMP,       "strcmp");
  dmnsn_astnode_map(DMNSN_AST_STRLEN,       "strlen");
  dmnsn_astnode_map(DMNSN_AST_TAN,          "tan");
  dmnsn_astnode_map(DMNSN_AST_TANH,         "tanh");
  dmnsn_astnode_map(DMNSN_AST_VAL,          "val");
  dmnsn_astnode_map(DMNSN_AST_VAXIS_ROTATE, "vaxis_rotate");
  dmnsn_astnode_map(DMNSN_AST_VCROSS,       "vcross");
  dmnsn_astnode_map(DMNSN_AST_VDOT,         "vdot");
  dmnsn_astnode_map(DMNSN_AST_VLENGTH,      "vlength");
  dmnsn_astnode_map(DMNSN_AST_VNORMALIZE,   "vnormalize");
  dmnsn_astnode_map(DMNSN_AST_VROTATE,      "vrotate");

  dmnsn_astnode_map(DMNSN_AST_PI,    "pi");
  dmnsn_astnode_map(DMNSN_AST_TRUE,  "true");
  dmnsn_astnode_map(DMNSN_AST_FALSE, "false");
  dmnsn_astnode_map(DMNSN_AST_X,     "x");
  dmnsn_astnode_map(DMNSN_AST_Y,     "y");
  dmnsn_astnode_map(DMNSN_AST_Z,     "z");
  dmnsn_astnode_map(DMNSN_AST_T,     "t");

  dmnsn_astnode_map(DMNSN_AST_NEGATE,       "-");
  dmnsn_astnode_map(DMNSN_AST_DOT_X,        ".x");
  dmnsn_astnode_map(DMNSN_AST_DOT_Y,        ".y");
  dmnsn_astnode_map(DMNSN_AST_DOT_Z,        ".z");
  dmnsn_astnode_map(DMNSN_AST_DOT_T,        ".t");
  dmnsn_astnode_map(DMNSN_AST_DOT_TRANSMIT, ".transmit");

  dmnsn_astnode_map(DMNSN_AST_IDENTIFIER, "identifier");

  dmnsn_astnode_map(DMNSN_AST_STRING, "string");

  dmnsn_astnode_map(DMNSN_AST_ARRAY, "array");

  dmnsn_astnode_map(DMNSN_AST_MACRO, "macro");

  default:
    fprintf(stderr, "WARNING: unrecognised astnode type %d.\n",
            (int)astnode_type);
    return "unrecognized-astnode";
  }
}