#region using using System.Collections.Generic; using Irony.Compiler; using ScriptNET.Runtime; using System; using System.Linq; #endregion namespace ScriptNET.Ast { /// /// Qualified Name /// internal class ScriptQualifiedName : ScriptExpr { #region Members private string Identifier; private List Modifiers; private ScriptQualifiedName NamePart; private delegate void EvaluateFunction(IScriptContext context); private delegate void AssignFunction(object value, IScriptContext context); private EvaluateFunction evaluation; private AssignFunction assignment; #endregion #region Constructor public ScriptQualifiedName(AstNodeArgs args) : base(args) { if (ChildNodes.Count == 2 && ChildNodes[1].ChildNodes.Count == 0) { Identifier = ((Token)ChildNodes[0]).Text; evaluation = EvaluateIdentifier; assignment = AssignIdentifier; } else if (ChildNodes[0] is Token && ChildNodes[1].ChildNodes.Count != 0) { Identifier = (ChildNodes[0] as Token).Text; //NOTE: There might be two cases: // 1) a()[]...() // 2) a<>.(NamePart) Modifiers = new List(); foreach (ScriptAst node in ChildNodes[1].ChildNodes) Modifiers.Add(node); ScriptGenericsPostfix generic = Modifiers.FirstOrDefault() as ScriptGenericsPostfix; if (generic != null && Modifiers.Count == 1) { //Case 2 evaluation = EvaluateGenericType; assignment = null; } else { //Case 1 evaluation = EvaluateFunctionCall; assignment = AssignArray; } } else { NamePart = ChildNodes[0] as ScriptQualifiedName; Identifier = ((Token)ChildNodes[2]).Text; if (ChildNodes.Count == 4 && ChildNodes[3].ChildNodes.Count != 0) { Modifiers = new List(); foreach (ScriptAst node in ChildNodes[3].ChildNodes) { Modifiers.Add(node); } } evaluation = EvaluateNamePart; assignment = AssignNamePart; } } #endregion #region Public Methods public override void Evaluate(IScriptContext context) { evaluation(context); } public void Assign(object value, IScriptContext context) { assignment(value, context); } #endregion #region Identifier private void EvaluateIdentifier(IScriptContext context) { object result = GetIndentifierValue(context, Identifier); context.Result = result; } private static object GetIndentifierValue(IScriptContext context, string identifier) { object result = context.GetItem(identifier, false); if (result != RuntimeHost.NoVariable) return result; if (RuntimeHost.HasType(identifier)) return RuntimeHost.GetType(identifier); else return NameSpaceResolver.Get(identifier); } private void AssignIdentifier(object value, IScriptContext context) { context.SetItem(Identifier, value); } #endregion #region Single Call private void EvaluateGenericType(IScriptContext context) { ScriptGenericsPostfix genericPostfix = (ScriptGenericsPostfix)Modifiers.First(); Type genericType = GetIndentifierValue(context, genericPostfix.GetGenericTypeName(Identifier)) as Type; if (genericType == null || !genericType.IsGenericType) { throw new ScriptException("Given type is not generic"); } genericPostfix.Evaluate(context); context.Result = genericType.MakeGenericType((Type[])context.Result); } private void EvaluateFunctionCall(IScriptContext context) { EvaluateIdentifier(context); foreach (ScriptAst node in Modifiers) { ScriptFunctionCall funcCall = node as ScriptFunctionCall; if (funcCall != null) { IInvokable function = context.Result as IInvokable; if (function == null) throw new ScriptException("Is not a function type"); context.Result = CallFunction(function, funcCall, context); continue; } ScriptArrayResolution arrayResolution = node as ScriptArrayResolution; if (arrayResolution != null) { GetArrayValue(context.Result, arrayResolution, context); continue; } ScriptGenericsPostfix genericPostfix = node as ScriptGenericsPostfix; if (genericPostfix != null) { throw new NotSupportedException(); //genericPostfix.Evaluate(Context); //continue; } } } private void AssignArray(object value, IScriptContext context) { object obj = context.GetItem(Identifier, true); foreach (ScriptAst node in Modifiers) { ScriptFunctionCall functionCall = node as ScriptFunctionCall; if (functionCall != null) { obj = CallFunction(context.GetFunctionDefinition(Identifier), functionCall, context); continue; } ScriptArrayResolution arrayResolution = node as ScriptArrayResolution; if (arrayResolution != null) { SetArrayValue(obj, arrayResolution, context, value); continue; } ScriptGenericsPostfix genericPostfix = node as ScriptGenericsPostfix; if (genericPostfix != null) { throw new NotSupportedException(); } } } private static void SetArrayValue(object obj, ScriptArrayResolution scriptArrayResolution, IScriptContext context, object value) { scriptArrayResolution.Evaluate(context); object[] indexParameters = (object[])context.Result; object[] setterParameters = new object[indexParameters.Length + 1]; indexParameters.CopyTo(setterParameters, 0); setterParameters[indexParameters.Length] = value; IObjectBind setter = RuntimeHost.Binder.BindToIndex(obj, setterParameters, true); if (setter != null) { setter.Invoke(context, null); return; } throw MethodNotFoundException("setter", indexParameters); } private static void GetArrayValue(object obj, ScriptArrayResolution scriptArrayResolution, IScriptContext context) { scriptArrayResolution.Evaluate(context); object[] param = (object[])context.Result; IObjectBind indexBind = RuntimeHost.Binder.BindToIndex(obj, param, false); if (indexBind != null) { context.Result = indexBind.Invoke(context, null); } else { throw MethodNotFoundException("indexer[]", param); } } #endregion #region Name Part private void EvaluateNamePart(IScriptContext context) { NamePart.Evaluate(context); object obj = context.Result; if (Modifiers == null) { context.Result = GetMemberValue(obj, Identifier); return; } Type[] genericArguments = null; foreach (ScriptAst node in Modifiers) { //NOTE: Generic modifier should be the first among other modifiers in the list ScriptGenericsPostfix generic = node as ScriptGenericsPostfix; if (generic != null) { if (genericArguments != null) { throw new ScriptException("Wrong modifiers sequence"); } generic.Execute(context); genericArguments = (Type[])context.Result; continue; } ScriptFunctionCall functionCall = node as ScriptFunctionCall; if (functionCall != null) { CallClassMethod(obj, Identifier, functionCall, genericArguments, context); continue; } ScriptArrayResolution arrayResolution = node as ScriptArrayResolution; if (arrayResolution != null) { GetArrayValue(GetMemberValue(obj, Identifier), arrayResolution, context); continue; } } } private void AssignNamePart(object value, IScriptContext context) { NamePart.Evaluate(context); object obj = context.Result; if (Modifiers == null) { SetMember(context, obj, value); return; } //TODO: Bug, first evaluate get member, see unit test AssignmentToArrayObject string localIdentifier = Identifier; foreach (ScriptAst node in Modifiers) { ScriptFunctionCall scriptFunctionCall = node as ScriptFunctionCall; if (scriptFunctionCall != null) { if (localIdentifier != null) { CallClassMethod(obj, localIdentifier, scriptFunctionCall, null, context); obj = context.Result; localIdentifier = null; } else { IInvokable funcDef = obj as IInvokable; if (funcDef == null) throw new ScriptException("Attempt to invoke non IInvokable object."); obj = CallFunction(funcDef, scriptFunctionCall, context); } continue; } ScriptArrayResolution scriptArrayResolution = node as ScriptArrayResolution; if (scriptArrayResolution != null) { if (localIdentifier != null) { obj = GetMemberValue(obj, localIdentifier); localIdentifier = null; } SetArrayValue(obj, scriptArrayResolution, context, value); continue; } } } #endregion #region Call Function private static object CallFunction(IInvokable functionDefinition, ScriptFunctionCall scriptFunctionCall, IScriptContext context) { scriptFunctionCall.Evaluate(context); return functionDefinition.Invoke(context, (object[])context.Result); } private void CallClassMethod(object obj, string memeberInfo, ScriptFunctionCall scriptFunctionCall, Type[] genericArguments, IScriptContext context) { scriptFunctionCall.Evaluate(context); context.Result = CallAppropriateMethod(context, obj, memeberInfo, genericArguments, (object[])context.Result); } private static object CallAppropriateMethod(IScriptContext context, object obj, string Name, Type[] genericArguments, object[] param) { IObjectBind methodBind = methodBind = RuntimeHost.Binder.BindToMethod(obj, Name, genericArguments, param); if (methodBind != null) return methodBind.Invoke(context, null); throw MethodNotFoundException(Name, param); } #endregion #region Helpers private void SetMember(IScriptContext context, object obj, object value) { IMemberBind bind = RuntimeHost.Binder.BindToMember(obj, Identifier, true); if (bind == null) throw new ScriptIdNotFoundException(Identifier); bind.SetValue(value); context.Result = value; //Context.Result = RuntimeHost.Binder.Set(Identifier, obj, value); } private static object GetMemberValue(object obj, string memberInfo) { IMemberBind bind = RuntimeHost.Binder.BindToMember(obj, memberInfo, true); if (bind == null) throw new ScriptIdNotFoundException(memberInfo); return bind.GetValue(); //return RuntimeHost.Binder.Get(memberInfo, obj); } private static ScriptMethodNotFoundException MethodNotFoundException(string Name, object[] param) { string message = ""; foreach (object t in param) { if (t != null) { if (string.IsNullOrEmpty(message)) message += t.GetType().Name; else message += ", " + t.GetType().Name; } } return new ScriptMethodNotFoundException("Semantic: There is no method with such signature: " + Name + "(" + message + ")"); } #endregion } }