// Copyright (C) 2003, 2005 Daisuke Arai <darai@users.sourceforge.jp>
// 
// This program is part of Protra.
//
// Protra 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.
//
// This program 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/>.
// 
// $Id: ExpressionNode.cs,v 1.3 2007-12-27 08:34:39 panacoran Exp $

using System;
using System.Collections;

namespace Protra.Lib.Lang
{
	/// <summary>
	/// 式のノードを表すクラスです。
	/// </summary>
	public class ExpressionNode : Node
	{
		/// <summary>
		/// 代入される変数名
		/// </summary>
		private string name;
		/// <summary>
		/// 変数に対するインデックスとなるノードのリスト
		/// </summary>
		private ArrayList indexNodeList = new ArrayList();
		/// <summary>
		/// 式のノード
		/// </summary>
		private Node expNode;

		/// <summary>
		/// 文脈を解析します。
		/// </summary>
		/// <exception cref="Protra.Lib.Lang.ParseException">
		/// 文脈にエラーがある場合にthrowされます。
		/// </exception>
		/// <param name="context">コンテキスト</param>
		public override void Parse(Context context)
		{
			this.ContextToken = (ContextToken)context.Current;

			ContextToken rct = (ContextToken)context.Current;
			if(rct.IsVariableName)
			{
				name = rct.ToString();
				while(context.MoveNext())
				{
					ContextToken ct = (ContextToken)context.Current;
					string token = ct.ToString();
					switch(token)
					{
						case "[":
							context.MoveNext();
							Node node = new ExpressionNode();
							node.Parse(context);
							indexNodeList.Add(node);
							ct = (ContextToken)context.Current;
							token = ct.ToString();
							if(token != "]")
								throw new ParseException("']' expected", this.ContextToken);
							break;
						case "=":
							context.MoveNext();
							expNode = new ExpressionNode();
							expNode.Parse(context);
							return;
						default:
							name = null;
							indexNodeList.Clear();
							context.Reset(rct);
							expNode = new LogicalExpressionNode();
							expNode.Parse(context);
							return;
					}
				}
			}
			else
			{
				expNode = new LogicalExpressionNode();
				expNode.Parse(context);
			}
		}

		/// <summary>
		/// 構文木を簡略化します。
		/// </summary>
		/// <returns>簡略化されたノード</returns>
		public override Node Simplify()
		{
			if(name == null)
				return expNode.Simplify();
			else
			{
				expNode = expNode.Simplify();
				for(int i = 0; i < indexNodeList.Count; i++)
				{
					Node node = (Node)indexNodeList[i];
					indexNodeList[i] = node.Simplify();
				}
				return this;
			}
		}

		/// <summary>
		/// プログラムを実行します。
		/// </summary>
		/// <exception cref="Protra.Lib.Lang.RuntimeException">
		/// プログラム実行中にエラーが発生した場合にthrowされます。
		/// </exception>
		/// <param name="resource">リソース</param>
		/// <param name="at">@作用素の値</param>
		/// <returns>実行結果</returns>
		public override Value Execute(Resource resource, int at)
		{
			Value val = expNode.Execute(resource, at);
			if(name != null) // 代入文
			{
				VariableTable lvt = resource.Stack.Top;
				if(indexNodeList.Count == 0) // 変数への代入 
				{
					if(name[0] == '$')
						resource.GlobalVariableTable[name] = val;
					else
						lvt[name] = val;
				}
				else // 配列への代入
				{
					Value arr;
					if(name[0] == '$')
						arr = resource.GlobalVariableTable[name];
					else
					{
						if(! lvt.ContainsName(name))
							throw new RuntimeException("undefined local variable '" + name + "'", this.ContextToken);
						arr = lvt[name];
					}

					Node node = (Node)indexNodeList[0];
					Value index = node.Execute(resource, at);

					for(int i = 1; i < indexNodeList.Count; i++)
					{
						arr = arr[index];
						node = (Node)indexNodeList[i];
						index = node.Execute(resource, at);
					}
					arr[index] = val;
				}
			}

			return val;
		}
	}
}
