text: ﻿/**
   * Copyright (c) 2015 QFish <im@qfi.sh>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  
  #include <assert.h>
  
  #include "tokenizer.h"
  #include "katana.tab.h"
  #include "katana.lex.h"
  #include "parser.h"
  #include "foundation.h"
  
  #undef	assert
  #define assert(x)
  
  static inline double katana_characters_to_double(const char* data, size_t length, bool* ok);
  static inline bool katana_is_html_space(char c);
  static inline char* katana_normalize_text(yy_size_t* length, char *origin_text, yy_size_t origin_length, int tok);
  
  #ifdef KATANA_FELX_DEBUG
  #if KATANA_FELX_DEBUG
  static char * katana_token_string(int tok);
  #endif // #if KATANA_FELX_DEBUG
  #endif // #ifdef KATANA_FELX_DEBUG
  
  /**
   *  A hook function of flex, processing tokens which will be passed to bison
   *
   *  @param yylval    the medium for flex and bison
   *  @param yyscanner flex state
   *  @param tok       the type of token
   *
   *  @return the type of token
   */
  int katana_tokenize(KATANASTYPE* lval , KATANALTYPE* loc, yyscan_t scanner, KatanaParser* parser, int tok)
  {
      char* origin_text = katanaget_text(scanner);
      
      yy_size_t len = 0;
      
      char* text = katana_normalize_text(&len, origin_text, katanaget_leng(scanner), tok);
      
  #ifdef KATANA_FELX_DEBUG
  #if KATANA_FELX_DEBUG
      if ( tok == KATANA_CSS_WHITESPACE ) {
          katana_parser_log(parser, "【%30s】=>【%30s】: %s", "", "", katana_token_string(tok));
      } else {
          katana_parser_log(parser, "【%30s】=>【%30s】: %s", origin_text, text, katana_token_string(tok));
      }
  #endif // #if KATANA_FELX_DEBUG
  #endif // #ifdef KATANA_FELX_DEBUG
      yy_size_t length = len;
      switch ( tok ) {
          case KATANA_CSS_INCLUDES:
          case KATANA_CSS_DASHMATCH:
          case KATANA_CSS_BEGINSWITH:
          case KATANA_CSS_ENDSWITH:
          case KATANA_CSS_CONTAINS:
              break;
          case KATANA_CSS_STRING:
          case KATANA_CSS_IDENT:
          case KATANA_CSS_NTH:
              
          case KATANA_CSS_HEX:
          case KATANA_CSS_IDSEL:
              
          case KATANA_CSS_DIMEN:
          case KATANA_CSS_INVALIDDIMEN:
          case KATANA_CSS_URI:
          case KATANA_CSS_FUNCTION:
          case KATANA_CSS_ANYFUNCTION:
          case KATANA_CSS_CUEFUNCTION:
          case KATANA_CSS_NOTFUNCTION:
          case KATANA_CSS_CALCFUNCTION:
          case KATANA_CSS_MINFUNCTION:
          case KATANA_CSS_MAXFUNCTION:
          case KATANA_CSS_HOSTFUNCTION:
          case KATANA_CSS_HOSTCONTEXTFUNCTION:
          case KATANA_CSS_UNICODERANGE:
          {
              lval->string.data = text;
              lval->string.length = length;
          }
              break;
              
          case KATANA_CSS_IMPORT_SYM:
          case KATANA_CSS_PAGE_SYM:
          case KATANA_CSS_MEDIA_SYM:
          case KATANA_CSS_SUPPORTS_SYM:
          case KATANA_CSS_FONT_FACE_SYM:
          case KATANA_CSS_CHARSET_SYM:
          case KATANA_CSS_NAMESPACE_SYM:
  //        case KATANA_CSS_VIEWPORT_RULE_SYM:
          case KATANA_INTERNAL_DECLS_SYM:
          case KATANA_INTERNAL_MEDIALIST_SYM:
          case KATANA_INTERNAL_RULE_SYM:
          case KATANA_INTERNAL_SELECTOR_SYM:
          case KATANA_INTERNAL_VALUE_SYM:
          case KATANA_INTERNAL_KEYFRAME_RULE_SYM:
          case KATANA_INTERNAL_KEYFRAME_KEY_LIST_SYM:
          case KATANA_INTERNAL_SUPPORTS_CONDITION_SYM:
          case KATANA_CSS_KEYFRAMES_SYM:
              break;
          case KATANA_CSS_QEMS:
              length--;
          case KATANA_CSS_GRADS:
          case KATANA_CSS_TURNS:
              length--;
          case KATANA_CSS_DEGS:
          case KATANA_CSS_RADS:
          case KATANA_CSS_KHERTZ:
          case KATANA_CSS_REMS:
              length--;
          case KATANA_CSS_MSECS:
          case KATANA_CSS_HERTZ:
          case KATANA_CSS_EMS:
          case KATANA_CSS_EXS:
          case KATANA_CSS_PXS:
          case KATANA_CSS_CMS:
          case KATANA_CSS_MMS:
          case KATANA_CSS_INS:
          case KATANA_CSS_PTS:
          case KATANA_CSS_PCS:
              length--;
          case KATANA_CSS_SECS:
          case KATANA_CSS_PERCENTAGE:
              length--;
          case KATANA_CSS_FLOATTOKEN:
              lval->number.val = katana_characters_to_double(text, length, NULL);
              lval->number.raw.data = text;
              lval->number.raw.length = len;
              break;
          case KATANA_CSS_INTEGER:
              lval->number.val = (int)katana_characters_to_double(text, length, NULL);
              lval->number.raw.data = text;
              lval->number.raw.length = len;
  
              break;
          default:
              break;
      }
      
      return tok;
  }
  
  /**
   *  Format token
   *
   *  @param length
   *  @param origin_text   original text from the flex
   *  @param origin_length formatted length
   *  @param tok
   *
   *  @return normalized text
   */
  static inline char* katana_normalize_text(yy_size_t* length, char *origin_text, yy_size_t origin_length, int tok)
  {
      char * start = origin_text;
      yy_size_t l = origin_length;
      switch ( tok ) {
          case KATANA_CSS_STRING:
              l--;
              /* nobreak */
          case KATANA_CSS_HEX:
          case KATANA_CSS_IDSEL:
              start++;
              l--;
              break;
          case KATANA_CSS_URI:
              // "url("{w}{string}{w}")"
              // "url("{w}{url}{w}")"
              // strip "url(" and ")"
              start += 4;
              l -= 5;
              // strip {w}
              while (l && katana_is_html_space(*start)) {
                  ++start;
                  --l;
              }
              while (l && katana_is_html_space(start[l - 1]))
                  --l;
              if (l && (*start == '"' || *start == '\'')) {
                  assert(l >= 2 && start[l - 1] == *start);
                  ++start;
                  l -= 2;
              }
              break;
          default:
              break;
      }
      
      *length = l;
      return start;
  }
  
  double katana_characters_to_double(const char* data, size_t length, bool* ok)
  {
      if (!length) {
          if (ok)
              *ok = false;
          return 0.0;
      }
      
      char* bytes = malloc(sizeof(char) * (length + 1));
      for (unsigned i = 0; i < length; ++i)
          bytes[i] = data[i] < 0x7F ? data[i] : '?';
      bytes[length] = '\0';
      char* end;
      double val = strtod(bytes, &end);
      if (ok)
          *ok = (end == 0 || *end == '\0');
  	free(bytes);
      return val;
  }
  
  #ifdef KATANA_FELX_DEBUG
  #if KATANA_FELX_DEBUG
  static char * katana_token_table[] = {
      "TOKEN_EOF", "LOWEST_PREC", "UNIMPORTANT_TOK",
      "KATANA_CSS_SGML_CD", "KATANA_CSS_WHITESPACE", "KATANA_CSS_INCLUDES",
      "KATANA_CSS_DASHMATCH", "KATANA_CSS_BEGINSWITH", "KATANA_CSS_ENDSWITH",
      "KATANA_CSS_CONTAINS", "KATANA_CSS_STRING", "KATANA_CSS_IDENT",
      "KATANA_CSS_NTH", "KATANA_CSS_HEX", "KATANA_CSS_IDSEL", "KATANA_CSS_IMPORT_SYM", "KATANA_CSS_PAGE_SYM",
      "KATANA_CSS_MEDIA_SYM", "KATANA_CSS_SUPPORTS_SYM",
      "KATANA_CSS_FONT_FACE_SYM", "KATANA_CSS_CHARSET_SYM",
      "KATANA_CSS_NAMESPACE_SYM", "KATANA_INTERNAL_DECLS_SYM",
      "KATANA_INTERNAL_MEDIALIST_SYM", "KATANA_INTERNAL_RULE_SYM",
      "KATANA_INTERNAL_SELECTOR_SYM", "KATANA_INTERNAL_VALUE_SYM",
      "KATANA_INTERNAL_KEYFRAME_RULE_SYM",
      "KATANA_INTERNAL_KEYFRAME_KEY_LIST_SYM",
      "KATANA_INTERNAL_SUPPORTS_CONDITION_SYM", "KATANA_CSS_KEYFRAMES_SYM",
      "KATANA_CSS_ATKEYWORD", "KATANA_CSS_IMPORTANT_SYM",
      "KATANA_CSS_MEDIA_NOT", "KATANA_CSS_MEDIA_ONLY", "KATANA_CSS_MEDIA_AND",
      "KATANA_CSS_MEDIA_OR", "KATANA_CSS_SUPPORTS_NOT",
      "KATANA_CSS_SUPPORTS_AND", "KATANA_CSS_SUPPORTS_OR", "KATANA_CSS_REMS",
      "KATANA_CSS_CHS", "KATANA_CSS_QEMS", "KATANA_CSS_EMS", "KATANA_CSS_EXS",
      "KATANA_CSS_PXS", "KATANA_CSS_CMS", "KATANA_CSS_MMS", "KATANA_CSS_INS",
      "KATANA_CSS_PTS", "KATANA_CSS_PCS", "KATANA_CSS_DEGS", "KATANA_CSS_RADS",
      "KATANA_CSS_GRADS", "KATANA_CSS_TURNS", "KATANA_CSS_MSECS",
      "KATANA_CSS_SECS", "KATANA_CSS_HERTZ", "KATANA_CSS_KHERTZ",
      "KATANA_CSS_DIMEN", "KATANA_CSS_INVALIDDIMEN", "KATANA_CSS_PERCENTAGE",
      "KATANA_CSS_FLOATTOKEN", "KATANA_CSS_INTEGER", "KATANA_CSS_VW",
      "KATANA_CSS_VH", "KATANA_CSS_VMIN", "KATANA_CSS_VMAX", "KATANA_CSS_DPPX",
      "KATANA_CSS_DPI", "KATANA_CSS_DPCM", "KATANA_CSS_FR", "KATANA_CSS_URI",
      "KATANA_CSS_FUNCTION", "KATANA_CSS_ANYFUNCTION",
      "KATANA_CSS_CUEFUNCTION", "KATANA_CSS_NOTFUNCTION",
      "KATANA_CSS_CALCFUNCTION", "KATANA_CSS_MINFUNCTION",
      "KATANA_CSS_MAXFUNCTION", "KATANA_CSS_HOSTFUNCTION",
      "KATANA_CSS_HOSTCONTEXTFUNCTION", "KATANA_CSS_UNICODERANGE" };
  
  static char * katana_token_string(int tok)
  {
      if (tok > 257)
      {
          return katana_token_table[tok-257];
      }
      else if ( 0 == tok )
      {
          return katana_token_table[0];
      }
      else
      {
          // leak when KATANA_FELX_DEBUG
          char* unicode = malloc(2);
          unicode[0] = (char)tok;
          unicode[1] = '\0';
          return unicode;
      }
  }
  #endif // #if KATANA_FELX_DEBUG
  #endif // #ifdef KATANA_FELX_DEBUG
  
  inline bool katana_is_html_space(char c)
  {
      return c <= ' ' && (c == ' ' || c == '\n' || c == '\t' || c == '\r' || c == '\f');
  }
  
  //inline int katana_to_ascii_hex_value(char c)
  //{
  //    //    ASSERT(isASCIIHexDigit(c));
  //    return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF;
  //}
  
  //inline bool katana_is_ascii_hex_digit(char c)
  //{
  //    return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f');
  //}
  