/* ////////// LICENSE INFO ////////////////////

 * Copyright (C) 2013 by NYSOL CORPORATION
 *
 * Unless you have received this program directly from NYSOL pursuant
 * to the terms of a commercial license agreement with NYSOL, then
 * this program is licensed to you under the terms of the GNU Affero General
 * Public License (AGPL) as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF 
 * NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Please refer to the AGPL (http://www.gnu.org/licenses/agpl-3.0.txt)
 * for more details.

 ////////// LICENSE INFO ////////////////////*/
// =============================================================================
// kgCalParser.h kgcal,kgselで用いる構文解析パーサークラス
// =============================================================================
#pragma once
#include <boost/spirit/include/classic.hpp>
#include <boost/spirit/include/classic_utility.hpp>
#include <boost/spirit/include/classic_ast.hpp>
#include <kgFunction.h>

using namespace boost::spirit::classic;
using namespace std;

namespace kgmod { ////////////////////////////////////////////// start namespace

struct kgCalParser : public grammar<kgCalParser>{
public:
	// paserタグのセット
	// 目的：parse時に定数，項目，関数(演算子)の区別を構文木のノードに
	// 記録しておくため．setparamにてこのIDに基づき定数は値として評価し，
	// 項目はCSVデータにおける項目番号として評価し，そして関数・演算子は
	// 関数クラスのインスタンス化を行う．
	// これらの値は構文木ノード構造体(struct tree_node)のメンバーである
	// データ値構造体(struct node_val_data)のメンバーidにセットされる
	static const int exprID       =  0; // 0固定
	static const int constIDstr   =  1;
	static const int constIDreal  =  2;
	static const int constIDdate  =  3;
	static const int constIDtime  =  4;
	static const int constIDbool  =  5;
	static const int fieldIDstr   =  6;
	static const int fieldIDreal  =  7;
	static const int fieldIDdate  =  8;
	static const int fieldIDtime  =  9;
	static const int fieldIDbool  = 10;
	static const int pfieldIDstr  = 11;
	static const int pfieldIDreal = 12;
	static const int pfieldIDdate = 13;
	static const int pfieldIDtime = 14;
	static const int pfieldIDbool = 15;

	template<typename ScannerT> struct definition{

		// 項目値用(文字列,整数,実数,日付,時刻,bool)
		rule<ScannerT, parser_context<>, parser_tag<fieldIDstr > > field_str;
		rule<ScannerT, parser_context<>, parser_tag<fieldIDreal> > field_real;
		rule<ScannerT, parser_context<>, parser_tag<fieldIDdate> > field_date;
		rule<ScannerT, parser_context<>, parser_tag<fieldIDtime> > field_time;
		rule<ScannerT, parser_context<>, parser_tag<fieldIDbool> > field_bool;

		// 前行項目値用(文字列,整数,実数,日付,時刻,bool)
		rule<ScannerT, parser_context<>, parser_tag<pfieldIDstr > > pfield_str;
		rule<ScannerT, parser_context<>, parser_tag<pfieldIDreal> > pfield_real;
		rule<ScannerT, parser_context<>, parser_tag<pfieldIDdate> > pfield_date;
		rule<ScannerT, parser_context<>, parser_tag<pfieldIDtime> > pfield_time;
		rule<ScannerT, parser_context<>, parser_tag<pfieldIDbool> > pfield_bool;

		// 定数値用(文字列,整数,実数,日付,時刻,bool)
		rule<ScannerT, parser_context<>, parser_tag<constIDstr > > const_str;
		rule<ScannerT, parser_context<>, parser_tag<constIDreal> > const_real;
		rule<ScannerT, parser_context<>, parser_tag<constIDdate> > const_date;
		rule<ScannerT, parser_context<>, parser_tag<constIDtime> > const_time;
		rule<ScannerT, parser_context<>, parser_tag<constIDbool> > const_bool;

		//関数・演算子用
		rule<ScannerT, parser_context<>, parser_tag<exprID> >
			term1,term2,term3,term4,term5,term6,term7,term8,expr,
			function1,function2,fctr;
			
		// 文法定義(コンストラクタ)
		definition(const kgCalParser& self){

			// -----------------------------------------------------------------------
			// [項目の指定] $x{xxx}の部分
			// ${項目名}   : 数値, 12  12.1
			// $s{項目名}  : 文字, "abc"
			// $d{項目名}  : 日付, 20080912
			// $t{項目名}  : 時刻, 121522
			// -----------------------------------------------------------------------
			// 文字列項目指定
			field_str = discard_node_d[*space_p >> str_p("$s{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// 数値項目指定
			field_real = discard_node_d[*space_p >> str_p("${")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// 日付項目指定
			field_date =  discard_node_d[*space_p >> str_p("$d{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// 時刻項目指定
			field_time = discard_node_d[*space_p >> str_p("$t{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// bool項目指定
			field_bool = discard_node_d[*space_p >> str_p("$b{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// -----------------------------------------------------------------------
			// [前行項目の指定]
			// #{項目名}   : 数値, 12  12.1
			// #s{項目名}  : 文字, "abc"
			// #d{項目名}  : 日付, 20080912
			// #t{項目名}  : 時刻, 121522
			// -----------------------------------------------------------------------
			// 文字列項目指定
			pfield_str = discard_node_d[*space_p >> str_p("#s{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') | eps_p ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// 数値項目指定
			pfield_real = discard_node_d[*space_p >> str_p("#{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') | eps_p ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// 日付項目指定
			pfield_date =  discard_node_d[*space_p >> str_p("#d{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') | eps_p ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// 時刻項目指定
			pfield_time = discard_node_d[*space_p >> str_p("#t{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') | eps_p ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// bool項目指定
			pfield_bool = discard_node_d[*space_p >> str_p("#b{")]
				>> lexeme_d[leaf_node_d[ *(anychar_p-'}') | eps_p ]]
				>> discard_node_d[ch_p('}') >> *space_p];

			// -----------------------------------------------------------------------
			// [定数の指定]
			// const_str  : 文字列, "abc"
			// const_real : 数値, 12 12.1
			// const_date : 日付, 0d20080912
			// const_time : 日付, 0t20080912121102
			// const_bin  : 2進数, 0b110100
			// const_oct  : 8進数, 0b7022
			// const_hex  : 16進数,0x8bff
			// -----------------------------------------------------------------------
			// 文字列の指定 "xxx"の部分
			const_str  = discard_node_d[*space_p >> ch_p('\"')]
	  		>> lexeme_d[leaf_node_d[*(c_escape_ch_p-'\"')]]
				>> discard_node_d[ch_p('\"') >> *space_p];

			// 数値の指定
			const_real = discard_node_d[*space_p]
			  >> leaf_node_d[real_p]
			  >> discard_node_d[*space_p];

			// 日付の指定
			const_date = discard_node_d[*space_p >> str_p("0d")]
				>> lexeme_d[leaf_node_d[repeat_p(8)[digit_p]]]
				>> discard_node_d[*space_p];

			// 時刻の指定
			const_time = discard_node_d[*space_p >> str_p("0t")]
				>> lexeme_d[leaf_node_d[repeat_p(14)[digit_p] | repeat_p(6)[digit_p] ]]
				>> discard_node_d[*space_p];

			// boolの指定
			const_bool = discard_node_d[*space_p >> str_p("0b")]
				>> lexeme_d[leaf_node_d[ch_p('0') | ch_p('1')]]
				>> discard_node_d[*space_p];

			// -----------------------------------------------------------------------
			// [関数の指定] 
			// function1 : 引数をとる関数, fff(x1,x2,...)
			// function2 : 引数をとらない関数, fff()
			// -----------------------------------------------------------------------
			// 引数をとる関数
			function1 = root_node_d[leaf_node_d[
				   alpha_p >> *(alpha_p|digit_p|ch_p('_'))]]
				>> discard_node_d[ch_p('(') >> *space_p]
				>> ( expr % discard_node_d[ch_p(',') >> *space_p])
				>> discard_node_d[ch_p(')')];

			// 引数をとらない関数
			function2 = root_node_d[leaf_node_d[alpha_p >> *(alpha_p|digit_p)]]
				>> discard_node_d[str_p("(") >> *space_p >> str_p(")")];

			// 二項演算子の指定 (優先順位込み)
			expr = term7 % root_node_d[
			                 discard_node_d[*space_p] >>
			                 str_p("||")              >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
 			                 str_p("^^")              >>
			                 discard_node_d[*space_p]
			               ];

			term7= term4 % root_node_d[
			                 discard_node_d[*space_p] >>
			                 str_p("&&")              >>
			                 discard_node_d[*space_p]
			               ];

			term4= term3 % root_node_d[
			                 discard_node_d[*space_p] >>
			                 str_p("==")              >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
			                 str_p("!=")              >>
			                 discard_node_d[*space_p] 
			               ];

			term3= term2 % root_node_d[
			                 discard_node_d[*space_p] >>
			                 str_p(">=")              >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
			                 str_p("<=")              >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
			                 str_p(">")               >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
 			                 str_p("<")               >>
			                 discard_node_d[*space_p]
			               ];

			term2= term1 % root_node_d[
			                 discard_node_d[*space_p] >>
			                 str_p("+")              >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
			                 str_p("-")               >>
			                 discard_node_d[*space_p]
			               ];

			term1= fctr  % root_node_d[
			                 discard_node_d[*space_p] >>
			                 str_p("*")               >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
			                 str_p("/")               >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
			                 str_p("%")               >>
			                 discard_node_d[*space_p] |
			                 discard_node_d[*space_p] >>
 			                 str_p("^")               >>
			                 discard_node_d[*space_p]
			               ];

			fctr = function2  | function1  | const_date | const_time  |
 			       const_bool | const_real | const_str  |
			       field_str  | field_real | field_date | field_time | field_bool |
						 pfield_str |pfield_real |pfield_date |pfield_time |pfield_bool |
			       inner_node_d[discard_node_d[*space_p]>>'('>>discard_node_d[*space_p]>>expr>>')'>>discard_node_d[*space_p]>>discard_node_d[*space_p]];		
		}
		rule<ScannerT, parser_context<>, parser_tag<exprID> >
		const& start() const {return expr;}
	};
};

} //////////////////////////////////////////////////////////////////////////////

