internal static dynamic Resolve(object rawName, object rawScope) { if (rawName.GetType() == typeof(InstanceReference)) { var iref = (InstanceReference)rawName; var lval = CompilerServices.CompileExpression(iref.LValue, (NovaScope)rawScope); var gmArgs = new List <Expression>(); gmArgs.Add(Expression.Constant(lval, typeof(object))); return(Dynamic(typeof(object), new InteropBinder.GetMember(iref.Key, (NovaScope)rawScope), gmArgs)); } var name = (string)rawName; var scope = (NovaScope)rawScope; if (name.StartsWith("$") && name != "$:") { scope = scope.GlobalScope; name = name.Substring(1); } if (name.StartsWith("@") && scope["<nova_context_invokemember>"] != null) { if (name.StartsWith("@@")) { var _val = Resolve("self", scope); if (!(_val is NovaInstance)) { // native object? _val = Nova.Box((object)_val, scope); } var @class = ((NovaInstance)_val).Class; return (CompilerServices.CompileExpression( NovaExpression.Variable(NovaExpression.InstanceRef(Expression.Constant(@class), Expression.Constant(name.Substring(2)))), scope)); } return (CompilerServices.CompileExpression( NovaExpression.Variable( NovaExpression.InstanceRef(NovaExpression.Variable(Expression.Constant("self")), Expression.Constant(name.Substring(1)))), scope)); } var val = scope[name]; // The cast is needed here because if we get a non-nullable type (such as System.Int32) the check here will throw an exception. // By casting to System.Object we can avoid the exception since it is a boxed value that can be null. if ((object)val == null) { Type type; if ((type = NovaTypeResolver.Resolve(name)) != null) { var @class = NovaClass.BoxClass(type); scope.GlobalScope[@class.Name] = @class; val = @class; } } return(val); }
public NovaClass(string name, NovaClass parent, List <NovaFunction> classMethods, List <NovaFunction> instanceMethods) { Name = name; ClassMethods = new Dictionary <string, NovaMethodTable>(); classMethods.ForEach(func => AddMethod(ClassMethods, func)); if (!ClassMethods.ContainsKey("new")) { AddMethod(ClassMethods, new NovaFunction("new", new List <FunctionArgument>(), NovaExpression.NovaBlock( NovaExpression.Return(new List <FunctionArgument> { new FunctionArgument(null, NovaExpression.Variable(Expression.Constant("self"))) }), Expression.Label(NovaParser.ReturnTarget, Expression.Constant(null, typeof(object)))), new NovaScope())); } InstanceMethods = new Dictionary <string, NovaMethodTable>(); instanceMethods.ForEach(func => AddMethod(InstanceMethods, func)); UndefinedMethods = new List <string>(); RemovedMethods = new List <string>(); Context = new NovaScope(); Parent = parent; }
internal static dynamic DefineClass(object rawName, object rawParent, List <Expression> contents, object rawScope) { lock (_classDefineLock) { if (Resolve(rawName, rawScope) != null) { return(DefineCategory(Resolve(rawName, rawScope), contents, rawScope)); } var scope = (NovaScope)rawScope; var defineScope = _inClassDefine ? scope : scope.GlobalScope; _inClassDefine = true; NovaClass parent; if (rawParent == null) { if (scope.GlobalScope["Object"] == null) { scope.GlobalScope["Object"] = Nova.Box(typeof(object)); } parent = scope.GlobalScope["Object"]; } else { var dParent = Resolve(rawParent as string, scope); if (dParent == null) { _inClassDefine = false; return(null); } if (dParent is Type) { parent = Nova.Box(dParent); } else { parent = dParent as NovaClass; } if (parent == null) { _inClassDefine = false; return(null); } } var name = (string)rawName; _className = name; var @class = new NovaClass { Name = _className, Parent = parent }; var xScope = new NovaScope(scope); xScope["self"] = @class; xScope[_className] = @class; _currentClassScope = xScope; contents.ForEach(content => { if (content is IncludeExpression) { // We only include modules here so make sure this include references a module var names = ((IncludeExpression)content).Names; dynamic module = null; var index = 0; names.ForEach(mname => { if ((module is NovaModule)) { module = module.Context[mname]; } else if (index == 0) { module = scope[mname]; } index = index + 1; }); if (module != null) { if (module is NovaModule) { ((NovaModule)module).Contents.ForEach(mcon => { if (mcon is NovaFunction) { if ((mcon as NovaFunction).IsSingleton || (mcon as NovaFunction).Name == "new") { NovaClass.AddMethod(@class.ClassMethods, mcon as NovaFunction); } else { NovaClass.AddMethod(@class.InstanceMethods, mcon as NovaFunction); } if (@class.RemovedMethods.Contains((mcon as NovaFunction).Name)) { @class.RemovedMethods.Remove((mcon as NovaFunction).Name); } if (@class.UndefinedMethods.Contains((mcon as NovaFunction).Name)) { @class.UndefinedMethods.Remove((mcon as NovaFunction).Name); } } }); xScope.MergeWithScope(module.Context); } else if (module is NovaClass) { xScope[((NovaClass)module).Name] = module; } } } }); contents.ForEach(content => { if (!(content is IncludeExpression)) { var result = CompilerServices.CompileExpression(content, xScope); if (result is NovaFunction) { if ((result as NovaFunction).IsSingleton || (result as NovaFunction).Name == "new") { NovaClass.AddMethod(@class.ClassMethods, result as NovaFunction); } else { NovaClass.AddMethod(@class.InstanceMethods, result as NovaFunction); } if (@class.RemovedMethods.Contains((result as NovaFunction).Name)) { @class.RemovedMethods.Remove((result as NovaFunction).Name); } if (@class.UndefinedMethods.Contains((result as NovaFunction).Name)) { @class.UndefinedMethods.Remove((result as NovaFunction).Name); } } } }); if ([email protected]("new")) { NovaClass.AddMethod(@class.ClassMethods, new NovaFunction("new", new List <FunctionArgument>(), NovaExpression.NovaBlock( NovaExpression.Return(new List <FunctionArgument> { new FunctionArgument(null, NovaExpression.Variable(Expression.Constant("self"))) }), Expression.Label(NovaParser.ReturnTarget, Expression.Constant(null, typeof(object)))), new NovaScope())); } @class.Context = xScope; defineScope[@class.Name] = @class; _inClassDefine = false; return(@class); } }
internal static dynamic Assign(VariableExpression @var, dynamic value, E type, bool isConst, object rawScope) { var scope = (NovaScope)rawScope; var map = new Dictionary <ExpressionType, ExpressionType>(); map[E.AddAssign] = E.Add; map[E.AndAssign] = E.And; map[E.DivideAssign] = E.Divide; map[E.ExclusiveOrAssign] = E.ExclusiveOr; map[E.LeftShiftAssign] = E.LeftShift; map[E.ModuloAssign] = E.Modulo; map[E.MultiplyAssign] = E.Multiply; map[E.OrAssign] = E.Or; map[E.PowerAssign] = E.Power; map[E.RightShiftAssign] = E.RightShift; map[E.SubtractAssign] = E.Subtract; var incDecMap = new List <ExpressionType> { E.PreIncrementAssign, E.PreDecrementAssign, E.PostIncrementAssign, E.PostDecrementAssign }; if (@var.Name is InstanceReferenceExpression) { var iref = CompilerServices.CompileExpression(@var.Name as InstanceReferenceExpression, scope); var lval = CompilerServices.CompileExpression(iref.LValue, scope); if (map.ContainsKey(type)) { value = CompilerServices.CreateLambdaForExpression( NovaExpression.Binary( Expression.Constant(Resolve(CompilerServices.CompileExpression(iref, scope), scope)), Expression.Constant(value), map[type]))(); } if (incDecMap.Contains(type)) { var gmArgs = new List <Expression>(); gmArgs.Add(Expression.Constant(lval, typeof(object))); if (type == E.PreIncrementAssign || type == E.PreDecrementAssign) { var val = Resolve(CompilerServices.CompileExpression(iref, scope), scope); Assign(@var, 1, type == E.PreIncrementAssign ? E.AddAssign : E.SubtractAssign, false, rawScope); return(val); } Assign(@var, 1, type == E.PostIncrementAssign ? E.AddAssign : E.SubtractAssign, false, rawScope); return(Resolve(CompilerServices.CompileExpression(iref, scope), scope)); } var smArgs = new List <Expression>(); smArgs.Add(Expression.Constant(lval, typeof(object))); smArgs.Add(Expression.Constant(value, typeof(object))); return(Dynamic(typeof(object), new InteropBinder.SetMember(iref.Key, scope), smArgs)); } if (@var.HasSym) { var sym = @var.Sym; var symFound = false; while (scope.ParentScope != null) { scope = scope.ParentScope; if (scope[sym] != null) { symFound = true; break; } } if (!symFound) { scope = (NovaScope)rawScope; } if (map.ContainsKey(type)) { var nvalue = CompilerServices.CreateLambdaForExpression( NovaExpression.Binary(Expression.Constant(ResolveSymbol(sym, scope)), Expression.Constant(value), map[type]))(); scope[sym] = nvalue; return(nvalue); } if (incDecMap.Contains(type)) { if (type == E.PreIncrementAssign || type == E.PreDecrementAssign) { var val = ResolveSymbol(sym, scope); Assign(@var, 1, type == E.PreIncrementAssign ? E.AddAssign : E.SubtractAssign, false, rawScope); return(val); } Assign(@var, 1, type == E.PostIncrementAssign ? E.AddAssign : E.SubtractAssign, false, rawScope); return(ResolveSymbol(sym, scope)); } scope[sym] = value; if (isConst) { scope.Constants.Add(sym.Name); } return(value); } string name = CompilerServices.CompileExpression(@var.Name, scope); if (name.StartsWith("$") && name != "$:") { scope = scope.GlobalScope; name = name.Substring(1); } var found = false; if (name.StartsWith("@")) { if (scope["<nova_context_invokemember>"] != null) { var ivar = NovaExpression.Variable( NovaExpression.InstanceRef(NovaExpression.Variable(Expression.Constant("self")), Expression.Constant(name.Substring(1)))); if (map.ContainsKey(type)) { value = CompilerServices.CreateLambdaForExpression( NovaExpression.Binary(ivar, Expression.Constant(value), map[type]))(); } var assn = NovaExpression.Assign(NovaExpression.LeftHandValue(ivar), Expression.Constant(value)); return(CompilerServices.CompileExpression(assn, scope)); } found = true; name = name.Substring(1); } if (name == "self") { if (scope["<nova_context_selfname>"] != null && scope["<nova_context_selfscope>"] != null && scope["<nova_context_invokemember>"] != null) { name = scope["<nova_context_selfname>"]; scope = scope["<nova_context_selfscope>"]; found = true; } } while (scope.ParentScope != null && !found) { scope = scope.ParentScope; if (scope[name] != null) { found = true; break; } } if (!found) { scope = (NovaScope)rawScope; } if (map.ContainsKey(type)) { var nvalue = CompilerServices.CreateLambdaForExpression( NovaExpression.Binary(Expression.Constant(Resolve(name, scope)), Expression.Constant(value), map[type]))(); scope[name] = nvalue; return(nvalue); } if (incDecMap.Contains(type)) { if (type == E.PostIncrementAssign || type == E.PostDecrementAssign) { var val = Resolve(name, scope); Assign(@var, 1, type == E.PostIncrementAssign ? E.AddAssign : E.SubtractAssign, false, rawScope); return(val); } Assign(@var, 1, type == E.PreIncrementAssign ? E.AddAssign : E.SubtractAssign, false, rawScope); return(Resolve(name, scope)); } scope[name] = value; if (isConst) { scope.Constants.Add(name); } return(value); }