/*
* Copyright © 2011, Petro Protsyk, Denys Vuika
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
namespace Scripting.SSharp.Runtime
{
///
/// Script Scope represents a tree of local scopes.
/// Scopes stores variables and types tables.
///
/// Run-time queries ScriptScope through ScriptContext for:
///
/// - Resolving names of types and variables;
/// - Resolving names of functions;
/// - Adding new function into scope;
/// - Assigning values to variables.
///
///
[DebuggerDisplay("Scope, Parent={Parent}")]
[DebuggerTypeProxy(typeof(ScriptScopeDebugViewer))]
public class ScriptScope : IScriptScope
{
#region properties
///
/// Parent Scope of the current scope.
/// Null if this scope is a global (root).
///
public IScriptScope Parent { get; set; }
Dictionary _vars = new Dictionary();
#endregion
#region constructors
///
/// Default Constructor
///
public ScriptScope():
this(null)
{
}
public ScriptScope(IScriptScope parent)
{
Parent = parent;
}
#endregion
#region IScriptScope
///
/// Returns value of the variable. Throws ScriptIdNotFoundException
///
/// Variable ID
///
/// Value of the variable
public virtual object GetItem(string id, bool throwException)
{
object result = GetVariableInternal(id, true);
if (result == RuntimeHost.NoVariable && throwException)
{
throw new ScriptIdNotFoundException(string.Format(Strings.VariableNotFound, id));
}
return result;
}
///
/// Returns true if excatly this scope has variable with given id
///
///
///
public virtual bool HasVariable(string id)
{
return _vars.ContainsKey(id);
}
///
/// Searches the scope hierarchy for the given id,
/// should return NoVariable if it is not found
///
///
///
///
protected virtual object GetVariableInternal(string id, bool searchHierarchy)
{
IValueReference result;
if (_vars.TryGetValue(id, out result))
{
return result.Value;
}
if (!searchHierarchy) return RuntimeHost.NoVariable;
IScriptScope scope = Parent;
while(scope != null)
{
if (scope.HasVariable(id))
return scope.GetItem(id, true);
scope = scope.Parent;
}
return RuntimeHost.NoVariable;
}
///
/// Sets Item: variable or type
///
/// item's id
/// value
public virtual void SetItem(string id, object value)
{
IValueReference reference = value as IValueReference;
if (reference != null)
{
_vars[id] = reference;
//if (_vars.ContainsKey(id))
// _vars[id] = reference;
//else
// _vars.Add(id, reference);
return;
}
if (_vars.TryGetValue(id, out reference))
{
reference.Value = value;
}
else
{
_vars.Add(id, new ValueReference(id, value) { Scope = this });
}
}
public virtual IValueReference Ref(string id)
{
if (!HasVariable(id)) throw new ScriptIdNotFoundException(id);
return _vars[id];
}
public virtual void CreateVariable(string id, object value)
{
SetItem(id, value);
}
#endregion
#region Functions
///
/// Gets Invokable object (Function) by a given name
///
/// Name
///
public virtual IInvokable GetFunctionDefinition(string name)
{
object result = GetVariableInternal(name, false);
IInvokable function = result as IInvokable;
if (function != null) return function;
if (Parent != null)
return Parent.GetFunctionDefinition(name);
else
throw new ScriptIdNotFoundException(string.Format(Strings.FunctionNotFound, name));
}
#endregion
#region IDisposable Members
private bool _disposed;
protected bool Disposed
{
get
{
lock (this)
{
return _disposed;
}
}
}
public void Dispose()
{
lock (this)
{
if (_disposed == false)
{
Cleanup();
_disposed = true;
GC.SuppressFinalize(this);
}
}
}
~ScriptScope()
{
Cleanup();
}
protected virtual void Cleanup()
{
if (_vars != null)
{
foreach (IValueReference vr in _vars.Values)
vr.Remove();
_vars.Clear();
_vars = null;
}
Parent = null;
}
#endregion
#region VS Debugging
private class ScriptScopeDebugViewer
{
private readonly ScriptScope _scope;
public ScriptScopeDebugViewer(ScriptScope s)
{
_scope = s;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public IValueReference[] Variables
{
get
{
return _scope == null ? new IValueReference[0] : _scope._vars.Select(v => v.Value).ToArray();
}
}
private string FormatValue(IValueReference iValueReference)
{
if (iValueReference == null) return "Null";
if (iValueReference.Value == null) return "Null";
return iValueReference.Value.ToString();
}
}
#endregion
}
///
/// Default scope activator - used by framework to create ScriptScope by default
/// may be overriden in xml configuration
///
public class ScriptScopeActivator : IScopeActivator
{
#region IScopeActivator Members
public IScriptScope Create(IScriptScope parent, params object[] args)
{
if (args == null || args.Length == 0)
{
ScriptScope result = new ScriptScope(parent);
SetBaseItems(result);
return result;
}
throw new NotSupportedException();
}
private void SetBaseItems(ScriptScope result)
{
//Process only root scopes
if (result.Parent != null) return;
//Variables
result.SetItem("Scope", result);
result.SetItem("Compiler", RuntimeHost.Parser);
////Custom Functions
//AppendAst
result.SetItem(CustomFunctions.AppendFunc.FunctionName, CustomFunctions.AppendFunc.FunctionDefinition);
//ReplaceAst
result.SetItem(CustomFunctions.ReplaceFunc.FunctionName, CustomFunctions.ReplaceFunc.FunctionDefinition);
//eval
result.SetItem(CustomFunctions.EvalFunc.FunctionName, CustomFunctions.EvalFunc.FunctionDefinition);
//Console
result.SetItem(CustomFunctions.RunConsole.FunctionName, CustomFunctions.RunConsole.FunctionDefinition);
//Array
result.SetItem(CustomFunctions.ArrayFunc.FunctionName, CustomFunctions.ArrayFunc.FunctionDefinition);
//Char
result.SetItem(CustomFunctions.CharFunc.FunctionName, CustomFunctions.CharFunc.FunctionDefinition);
}
#endregion
}
}