Could you add these future to flee?

Jan 14, 2009 at 2:29 PM
First sorry for my pool english.

  1. Could a ExpressionContext or VariableCollection have a Parent?
    Let it provide a Scope future, if it can't find a variable or function in current context it can ask its parent for one.
    The event-drived-finding is hard to use.
  2. Could you privde a ScriptLanguageModel?
    Means let it more like javascript, the number 1 is equals the string "1",and 1 or 0 can use as bool true or false.

I have a last questions,Why you remove the ExpressionFactory? you can keep it. Now I must modify most of my code to update to 0.9.21.
Coordinator
Jan 15, 2009 at 11:46 PM
>Could a ExpressionContext or VariableCollection have a Parent?
Can you tell me more about what you're trying to accomplish?  This is a core feature and I want to make sure I get a good grasp of your requirements.

>Could you privde a ScriptLanguageModel?
Flee is designed around being a static language.  Making it work like a dynamic language would take a lot of effort which I can't dedicate right now.

>I have a last questions,Why you remove the ExpressionFactory?
Once I put the compile method on the expression context, the expression factory class wasn't needed anymore.  One of my goals for Flee is that it has a nice, clean api.  When I make a bad design decision, I usually break backwards compatibility in order to keep Flee's design clean.
Jan 16, 2009 at 2:33 AM
Edited Jan 16, 2009 at 2:44 AM
Thank you first.Look like my English is very bad. hehe.

At beginning,I want to find a Javascript Script Engine base on .Net(Like Java 6's Rhino Script Engine).
Finally I find your FLEE, but it's only a Expression Engine.
So I decided to use FLEE to explain expression, and I develop some WinForm control to implement the statement behavior(like for/while/foreach).
Normally, any variable in some statement scope( for(int i = 0;;) ) hide one with the same name outside, and if there is no variable with the name inside it will find it outside.
So I think the ExpressionContext or VariableCollection could have a parent.when current context can't find some variable or function it can ask its parent for it.Then I Just need a Stack, when I enter a scope I push a ExpressionContext in,and when I leave I just pop it out.
And I think it easy to use for someone who often work with multi context,he don't need to import any common type or function or shared variable for each ExpressionContext.

I'm not sure I clearly described what I'm thinking.and I wrote some code :

internal static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
private static void Main()
{
var context1 = new ExpressionContext();
context1.Variables.Add("v1", 1);
var context2 = new ExpressionContext();
context2.Variables.Add("v2", 2);
var context3 = new ExpressionContext();
context3.Variables.Add("v3", 3);
var scope = new ExpressionContextScope();
scope.Push(context1);
scope.Push(context2);
scope.Push(context3);
var dynamic = context3.CompileDynamic("v1");
Console.WriteLine(dynamic.Evaluate());

Console.Read();
}
}

public class ExpressionContextScope
{
readonly Stack<Node> _nodes = new Stack<Node>();

public ExpressionContext Peek()
{
return _nodes.Peek().Context;
}

public ExpressionContext Pop()
{
var pop = _nodes.Pop();
pop.Delink();
return pop.Context;
}

public void Push(ExpressionContext item)
{
if (_nodes.Count != 0)
_nodes.Push(new Node(item, _nodes.Peek().Context));
else
_nodes.Push(new Node(item, null));
}

private class Node
{
public Node(ExpressionContext context, ExpressionContext parent)
{
if (context == null) throw new ArgumentNullException("context");
Context = context;
if(parent != null)
{
Parent = parent;
Link();
}
}

public ExpressionContext Context { get; set; }

public ExpressionContext Parent { get; set; }

public void Link()
{
var variables = Context.Variables;
variables.ResolveFunction += ResolveFunction;
variables.InvokeFunction += InvokeFunction;
variables.ResolveVariableType += ResolveVariableType;
variables.ResolveVariableValue += ResolveVariableValue;
}

private void ResolveFunction(object sender, ResolveFunctionEventArgs e)
{
// There is no any method can provide the function's return type that containing in Parent context
// e.ReturnType = Parent.Variables.ResolveFunction(e.FunctionName, e.ArgumentTypes);
e.ReturnType = typeof (object);
}

private void InvokeFunction(object sender, InvokeFunctionEventArgs e)
{
// e.Result = Parent.Variables.GetFunctionResultInternal<object>(e.FunctionName, e.Arguments);
}

private void ResolveVariableType(object sender, ResolveVariableTypeEventArgs e)
{
// This doesn't works,because GetVariableType method doesn't occur the Parent's ResolveVariableType event.
// e.VariableType = Parent.Variables.GetVariableType(e.VariableName);

e.VariableType = typeof (object);
}

private void ResolveVariableValue(object sender, ResolveVariableValueEventArgs e)
{
// Because I can't find the type of Parent's variable, there code doesn't work.
// var method = Parent.Variables.GetType().GetMethod("GetVariableValueInternal").MakeGenericMethod(e.VariableType);
// e.VariableValue = method.Invoke(Parent.Variables, new object[] {e.VariableName});
}

public void Delink()
{
var variables = Context.Variables;
variables.ResolveFunction -= ResolveFunction;
variables.InvokeFunction -= InvokeFunction;
variables.ResolveVariableType -= ResolveVariableType;
variables.ResolveVariableValue -= ResolveVariableValue;
}
}
}






I agree with you to keep API clean.But remove some important part may cause some trouble.Fix code is not interesting.

Last question : There are some name of methods in VariableCollection end with Internal,Why they are public, not internal ?

And Thinks again.
Jan 16, 2009 at 6:23 AM
public class ExpressionContextScope
{
private readonly Stack<Node> _nodes = new Stack<Node>();

public ExpressionContext Peek()
{
return _nodes.Peek().Context;
}

public ExpressionContext Pop()
{
var pop = _nodes.Pop();
pop.Delink();
return pop.Context;
}

public void Push(ExpressionContext item)
{
if (_nodes.Count != 0)
_nodes.Push(new Node(item, _nodes.Peek()));
else
_nodes.Push(new Node(item, null));
}

private class Node
{
public Node(ExpressionContext context, Node parent)
{
if (context == null) throw new ArgumentNullException("context");
Context = context;
if (parent == null) return;
Parent = parent;
Link();
}

public ExpressionContext Context { get; set; }

public Node Parent { get; set; }

public void Link()
{
var variables = Context.Variables;
variables.ResolveVariableType += ResolveVariableType;
variables.ResolveVariableValue += ResolveVariableValue;
}

public void Delink()
{
var variables = Context.Variables;
variables.ResolveVariableType -= ResolveVariableType;
variables.ResolveVariableValue -= ResolveVariableValue;
}

public Type GetVariableType(string name)
{
if (Context.Variables.ContainsKey(name))
return Context.Variables[name].GetType();
if (Parent != null) return Parent.GetVariableType(name);
throw new InvalidOperationException("变量不存在");
}


private object GetVariableValue(string name)
{
if (Context.Variables.ContainsKey(name))
return Context.Variables[name];
if (Parent != null) return Parent.GetVariableValue(name);
throw new InvalidOperationException("变量不存在");
}

private void ResolveVariableType(object sender, ResolveVariableTypeEventArgs e)
{
e.VariableType = GetVariableType(e.VariableName);
}

private void ResolveVariableValue(object sender, ResolveVariableValueEventArgs e)
{
e.VariableValue = GetVariableValue(e.VariableName);
}
}
}





This class resovled my problem, but I still can't find a way to invoke functions in parent.

And there is a small bug in VariableCollection.TryGetValue(), local variable "bool flag" doesn't do his duty. it always be false.