/******************************************************************************
 * mod_uploader / TemplateParser.h
 ******************************************************************************
 * Copyright (C) 2005 Tetsuya Kimata <kimata@acapulco.dyndns.org>
 *
 * All rights reserved.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must
 *    not claim that you wrote the original software. If you use this
 *    software in a product, an acknowledgment in the product
 *    documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and must
 *    not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 *    distribution.
 *
 * $Id: TemplateParser.h 466 2005-07-17 05:11:07Z svn $
 *****************************************************************************/

#ifndef TEMPLATEPARSER_H
#define TEMPLATEPARSER_H

#include "TemplateLexer.h"

#include "apr_pools.h"

#include <cstdlib>
#include <vector>

#ifdef HAVE_CONFIG_H
#include "mod_uploader_config.h"
#endif

using namespace std;

/**
 * @brief テンプレートをパースするクラス．
 *
 * ASSIGN　と VARIABLE 周りで手を抜いているので汚いです．
 *
 * <pre>
 * PROGRAM    ::= STMT_LIST
 * STMT_LIST  ::= STMT*
 * STMT_LIST  ::= STMT_LIST STMT | STMT
 * STMT       ::= FOREACH | WHILE | IF | PRINT | EXPR ';'
 * FOREACH    ::= 'foreach' '(' IDENTIFIER 'in' VARIABLE ')' '{' STMT_LIST  '}'
 * WHILE      ::= 'while' '(' COMPARE ')' '{' STMT_LIST  '}'
 * IF         ::= 'if' '(' COMPARE ')' '{' STMT_LIST '}' ('else' '{' STMT_LIST '}')?
 * PRINT      ::= 'print' EXPR_LIST ';'
 * EXPR_LIST  ::= EXPR (',' EXPR)*
 * EXPR       ::= ASSIGN | COMPARE
 * ASSIGN     ::= IDENTIFIER ASSIGN_OP COMPARE
 * COMPARE    ::= ARITHMETIC COMPARE_OP ARITHMETIC | ARITHMETIC
 * ARITHMETIC ::= ARITHMETIC '+' MULTIPLY | ARITHMETIC '-' MULTIPLY |
 *                MULTIPLY
 * MULTIPLY   ::= MULTIPLY '%' TERM |
 *                TERM
 * TERM       ::= MONADIC_OP VARIABLE | VARIABLE MONADIC_OP |
 *                VARIABLE | INTEGER | '(' EXPR ')'
 * VARIABLE   ::= IDENTIFIER |
 *                IDENTIFIER '.' IDENTIFIER |
 *                IDENTIFIER '[' (INTEGER | IDENTIFIER) ']' |
 *                IDENTIFIER '[' (INTEGER | IDENTIFIER) ']' '.' IDENTIFIER
 * </pre>
 *
 * 左再帰を排除したバージョン．
 *
 * <pre>
 * PROGRAM    ::= STMT_LIST
 * STMT_LIST  ::= STMT*
 * STMT       ::= FOREACH | IF | PRINT | EXPR ';'
 * FOREACH    ::= 'foreach' '(' IDENTIFIER 'in' VARIABLE ')' '{' STMT_LIST  '}'
 * WHILE      ::= 'while' '(' COMPARE ')' '{' STMT_LIST  '}'
 * IF         ::= 'if' '(' ARITHMETIC COMPARE_ ')' '{' STMT_LIST '}' ELSE_
 * ELSE_      ::= 'else' '{' STMT_LIST '}' | ε
 * PRINT      ::= 'print' EXPR_LIST ';'
 * EXPR_LIST  ::= EXPR (',' EXPR)*
 * EXPR       ::= ASSIGN | COMPARE
 * ASSIGN     ::= IDENTIFIER ASSIGN_OP COMPARE
 * COMPARE    ::= ARITHMETIC COMPARE_
 * COMPARE_   ::= COMPARE_OP ARITHMETIC | ε
 * ARITHMETIC ::= MULTIPLY ARITHMETIC_
 * ARITHMETIC_::= '+' MULTIPLY ARITHMETIC_ | '-' MULTIPLY ARITHMETIC_ | ε
 * MULTIPLY   ::= TERM MULTIPLY_
 * MULTIPLY_  ::= '%' TERM MULTIPLY_ | ε
 * TERM       ::= MONADIC_OP VARIABLE | VARIABLE MONADIC_OP |
 *                VARIABLE | INTEGER | STRING | '(' EXPR ')'
 * VARIABLE   ::= IDENTIFIER VARIABLE_
 * VARIABLE_  ::= '.' IDENTIFIER | '[' (INTEGER | IDENTIFIER) ']' VARIABLE__ | ε
 * VARIABLE__ ::= '.' IDENTIFIER | ε
 * </pre>
 */
class TemplateParser
{
public:
    enum node_type {
        FOREACH,                // foreach
        WHILE,                  // while
        IF,                     // if
        PRINT,                  // print

        STRING,                 // 文字列
        IDENTIFIER,             // 識別子
        INTEGER,                // 整数

        ARRAY_REF,              // []
        HASH_REF,               // .

        ASSIGNMENT,             // =
        PLUS_ASSIGNMENT,        // +=
        MINUS_ASSIGNMENT,       // -=

        EQUAL,                  // ==
        NOTEQUAL,               // !=
        GREATER_THAN,           // >
        LESS_THAN,              // <

        PLUS,                   // +
        MINUS,                  // -
        RESIDUE,                // %
        PLUSPLUS,               // ++
        MINUSMINUS,             // --

        BANK,

        DEFAULT
    };

    typedef TemplateLexer::token_vector token_vector;
    typedef TemplateLexer::token token;
    typedef TemplateLexer::ident_map ident_map;

    struct Node;

    struct Branch {
        struct Node *left;
        struct Node *center;
        struct Node *right;
    };

    typedef struct Node {
        node_type type;
#ifdef DEBUG
        apr_size_t id;
#endif
        struct Branch branch;
        union {
            int i_val;
            const char *s_val;
        };
    } node;

    TemplateParser(apr_pool_t *pool);
    node *parse(token_vector* tvector, ident_map *imap);

#ifdef DEBUG
    static void print_dot(node *node);
    static apr_size_t print_node(node *node);
    static const char *get_node_type(node * const node);
#endif

private:
    static const apr_size_t NODE_POOL_NUM;

    typedef struct Handle {
        token_vector::iterator token;
        token_vector::const_iterator token_start;
        token_vector::const_iterator token_end;
        ident_map * const imap;

        Handle(token_vector* tvector, ident_map *ident_map)
            : imap(ident_map)
        {
            token       = tvector->begin();
            token_start = tvector->begin();
            token_end   = tvector->end();
        }
    } handle;

#ifdef DEBUG
    typedef struct {
        node_type type;
        const char *str;
    } node_type_str;

    static node_type_str node_type_list[];
#endif

    apr_pool_t *pool_;
    node *node_pool_;
    node *node_pool_end_;

    TemplateParser(const TemplateParser&);
    TemplateParser &operator=(const TemplateParser&);

    node *parse_program(handle *h);
    node *parse_stmt_list(handle *h);
    node *parse_stmt(handle *h);
    node *parse_foreach(handle *h);
    node *parse_while(handle *h);
    node *parse_if(handle *h);
    node *parse_else_(handle *h);
    node *parse_print(handle *h);
    node *parse_expr_list(handle *h);
    node *parse_expr(handle *h);
    node *parse_assign(handle *h);
    node *parse_compare(handle *h);
    node *parse_compare_(handle *h);
    node *parse_arithmetic(handle *h);
    node *parse_arithmetic_(handle *h);
    node *parse_multiply(handle *h);
    node *parse_multiply_(handle *h);
    node *parse_term(handle *h);
    node *parse_variable(handle *h);
    node *parse_variable_(handle *h);
    node *parse_variable__(handle *h);

    node *create_node(node_type type=DEFAULT);
    node *create_node(TemplateLexer::token_type type);
};

#endif

// Local Variables:
// mode: c++
// buffer-file-coding-system: euc-japan-dos
// End:
