/// <summary> /// Executes a while statement node. /// </summary> public void ExecuteWhileStatement(Node node, ScopeContext scopeContext) { // Loop until an exit condition is reached. while (true) { var conditionResult = CheckCondition(node.Nodes[0].Nodes[0], scopeContext); // Exit when the condition is false. if (!conditionResult) { break; } var scope = ExecuteBlock(node.Nodes[1], scopeContext, ScopeProperties.InLoop); // Exit when a break expression was reached. if ((scope.Properties & ScopeProperties.BrokeLoop) != 0) { break; } if (scope.ReturnObject != null) { scopeContext.ReturnObject = scope.ReturnObject; break; } } }
/// <summary> /// Executes an index opeation. /// </summary> public SkryptObject ExecuteIndex(IndexNode node, ScopeContext scopeContext) { var arguments = new List <SkryptObject>(); foreach (var subNode in node.Arguments) { var result = ExecuteExpression(subNode, scopeContext); arguments.Add(result); } var Object = ExecuteExpression(node.Getter, scopeContext); // Dynamically change type so we can get it's actual operations. dynamic left = Convert.ChangeType(Object, Object.GetType()); Operation opLeft = SkryptObject.GetOperation(Operators.Index, Object.GetType(), arguments[0].GetType(), left.Operations); OperationDelegate operation = null; if (opLeft != null) { operation = opLeft.OperationDelegate; } else { _engine.ThrowError("No such operation as index " + left.Name + "!", node.Getter.Token); } var inputArray = new List <SkryptObject>(arguments); inputArray.Insert(0, Object); return(operation(inputArray.ToArray(), _engine)); }
/// <summary> /// Solves an expression and returns its boolean result. /// </summary> private bool CheckCondition(Node node, ScopeContext scopeContext) { var conditionResult = false; conditionResult = _engine.Executor.ExecuteExpression(node, scopeContext).ToBoolean(); return(conditionResult); }
/// <summary> /// Executes a for statement node. /// </summary> public void ExecuteForStatement(Node node, ScopeContext scopeContext) { var initNode = node.Nodes[0]; var condNode = node.Nodes[1]; var modiNode = node.Nodes[2]; var block = node.Nodes[3]; ScopeContext loopScope = new ScopeContext(); loopScope.StrictlyLocal = true; if (scopeContext != null) { loopScope.ParentScope = scopeContext; scopeContext.SubScopes.Add(loopScope); } var initResult = ExecuteExpression(initNode, loopScope); if (scopeContext != null) { loopScope.Properties = scopeContext.Properties; } loopScope.Properties |= ScopeProperties.InLoop; loopScope.StrictlyLocal = false; // Loop until an exit condition is reached. while (true) { var conditionResult = CheckCondition(condNode, loopScope); // Exit when the condition is false. if (!conditionResult) { break; } var scope = ExecuteBlock(block, loopScope); // Exit when a break expression was reached. if ((scope.Properties & ScopeProperties.BrokeLoop) != 0) { break; } if (scope.ReturnObject != null) { scopeContext.ReturnObject = scope.ReturnObject; break; } var modiResult = ExecuteExpression(modiNode, loopScope); } }
/// <summary> /// Executes an if statement node. /// </summary> public void ExecuteIfStatement(Node node, ScopeContext scopeContext) { var conditionResult = CheckCondition(node.Nodes[0].Nodes[0], scopeContext); // Execute body of statement if its evaluated to true. if (conditionResult) { var scope = ExecuteBlock(node.Nodes[1], scopeContext); if (scope.ReturnObject != null) { scopeContext.ReturnObject = scope.ReturnObject; } return; } // Execute elseif/else statements proceeding the current statement. if (node.Nodes.Count > 2) { for (var i = 2; i < node.Nodes.Count; i++) { var elseNode = node.Nodes[i]; // Execute elseif statement as if statement. if (elseNode.Body == "elseif") { conditionResult = _engine.Executor.ExecuteExpression(elseNode.Nodes[0].Nodes[0], scopeContext).ToBoolean(); if (conditionResult) { var scope = ExecuteBlock(elseNode.Nodes[1], scopeContext); if (scope.ReturnObject != null) { scopeContext.ReturnObject = scope.ReturnObject; } return; } } // Execute remaining else statement. else { var scope = ExecuteBlock(elseNode, scopeContext); if (scope.ReturnObject != null) { scopeContext.ReturnObject = scope.ReturnObject; } } } } }
/// <summary> /// Executes a using statement. /// </summary> public ScopeContext ExecuteUsing(ImportNode node, ScopeContext scopeContext) { var Object = ExecuteExpression(node.Getter, scopeContext); // Add all public variables from object as variables to scope. foreach (var property in Object.Properties) { if ((property.Modifiers & Modifier.Public) != 0) { scopeContext.SetVariable(property.Name, property.Value, Modifier.Const); } } return(scopeContext); }
/// <summary> /// Recursively fetches a variable from the given scope. /// </summary> public Variable GetVariable(string name, ScopeContext scopeContext) { Variable foundVar = null; if (scopeContext.Variables.ContainsKey(name)) { foundVar = scopeContext.Variables[name]; } else if (scopeContext.ParentScope != null) { foundVar = GetVariable(name, scopeContext.ParentScope); } return(foundVar); }
/// <summary> /// Executes a function definition node. /// </summary> public UserFunction ExecuteFunctionDeclaration(Node node, ScopeContext scopeContext) { UserFunction result = new UserFunction { Name = node.Body, Signature = node.Body, BlockNode = node.Nodes[0], CallName = node.Body, Path = _engine.CurrentExecutingFile }; // Create parameters. foreach (Node snode in node.Nodes[1].Nodes) { result.Parameters.Add(snode.Body); } return(result); }
/// <summary> /// Recursively fetches a type from the given scope. /// </summary> public SkryptObject GetType(string name, ScopeContext scopeContext) { SkryptObject foundVar = null; if (scopeContext.Types.ContainsKey(name)) { foundVar = scopeContext.Types[name]; } else if (scopeContext.ParentScope != null) { foundVar = GetType(name, scopeContext.ParentScope); } if (foundVar == null) { _engine.ThrowError("Could not find type: " + name); } return(foundVar); }
/// <summary> /// Executes a block. /// </summary> public ScopeContext ExecuteBlock(Node node, ScopeContext scopeContext, ScopeProperties properties = 0) { _engine.State = EngineState.Executing; ScopeContext scope = new ScopeContext(); // Copy settings from previous scope. if (scopeContext != null) { scope.Properties = scopeContext.Properties; scope.ParentScope = scopeContext; scope.Types = scopeContext.Types; scope.CallStack = scopeContext.CallStack; scope.ParentClass = scopeContext.ParentClass; scopeContext.SubScopes.Add(scope); } if (properties != 0) { scope.Properties |= scopeContext.Properties; } if ((scope.Properties & ScopeProperties.InClassDeclaration) == 0) { if ((node.Modifiers & Modifier.Instance) != 0 || (node.Modifiers & Modifier.Public) != 0 || (node.Modifiers & Modifier.Private) != 0) { _engine.ThrowError("Property modifiers cannot be used outside class", node.Token); } } _engine.CurrentScope = scope; var oldStack = _engine.CurrentStack; var oldPath = _engine.CurrentExecutingFile; foreach (var subNode in node.Nodes) { if (subNode.Type == TokenTypes.Statement) { switch (subNode.Body) { case "while": ExecuteWhileStatement(subNode, scope); break; case "if": ExecuteIfStatement(subNode, scope); break; case "for": ExecuteForStatement(subNode, scope); break; } if ((scope.Properties & ScopeProperties.SkippedLoop) != 0) { return(scope); } if ((scope.Properties & ScopeProperties.BrokeLoop) != 0) { return(scope); } if (scope.ReturnObject != null) { return(scope); } } else if (subNode.Type == TokenTypes.Include) { _engine.CurrentExecutingFile = ((IncludeNode)subNode).Path; scope = ExecuteBlock(subNode, scope); } else if (subNode.Type == TokenTypes.MethodDeclaration) { var result = ExecuteFunctionDeclaration(subNode, scope); scope.SetVariable(result.CallName, result, subNode.Modifiers); } else if (subNode.Type == TokenTypes.ClassDeclaration) { var createdClass = ExecuteClassDeclaration((ClassNode)subNode, scope); scope.SetVariable(createdClass.Name, createdClass, subNode.Modifiers); } else if (subNode.Type == TokenTypes.Import) { var _scope = ExecuteUsing((ImportNode)subNode, scope); } else { var result = _engine.Executor.ExecuteExpression(subNode, scope); if ((scope.Properties & ScopeProperties.SkippedLoop) != 0) { return(scope); } if ((scope.Properties & ScopeProperties.BrokeLoop) != 0) { return(scope); } if (scope.ReturnObject != null) { return(scope); } } // Reset current stack and scope variables. _engine.CurrentScope = scope; _engine.CurrentStack = oldStack; _engine.CurrentExecutingFile = oldPath; } return(scope); }
/// <summary> /// Executes a class definition node. /// </summary> public SkryptObject ExecuteClassDeclaration(ClassNode node, ScopeContext scopeContext) { string ClassName = node.Name; var ParentClass = scopeContext.ParentClass; if (ParentClass != null) { ClassName = ParentClass.Name + "." + ClassName; } // The static object. SkryptObject Object = new SkryptObject { Name = ClassName, }; // The object base that gets instantiated. SkryptType TypeObject = new SkryptType { Name = ClassName, }; scopeContext.AddType(ClassName, TypeObject); // Give objects a value displaying their type. Object.Properties.Add(new SkryptProperty { Name = "TypeName", Value = new Library.Native.System.String(ClassName), Modifiers = Parsing.Modifier.Const }); TypeObject.Properties.Add(new SkryptProperty { Name = "TypeName", Value = new Library.Native.System.String(ClassName), Modifiers = Parsing.Modifier.Const }); // Give instance object a value pointing to its base type. TypeObject.Properties.Add(new SkryptProperty { Name = "Type", Value = Object, Modifiers = Parsing.Modifier.Const }); // Execute class body. var scope = ExecuteBlock(node.BodyNode, scopeContext, ScopeProperties.InClassDeclaration); // Add variables with modifiers to instance and base objects. foreach (var v in scope.Variables) { if (v.Value.Modifiers != Modifier.None) { var property = new SkryptProperty { Name = v.Key, Value = v.Value.Value, Modifiers = v.Value.Modifiers }; if ((v.Value.Modifiers & Modifier.Instance) == 0) { var find = TypeObject.Properties.Find(x => x.Name == v.Key); if (find != null) { find = property; } else { Object.Properties.Add(property); } } else { var find = TypeObject.Properties.Find(x => x.Name == v.Key); if (find != null) { find = property; } else { TypeObject.Properties.Add(property); } } } } // Add variables from inherited classes to objects. foreach (var inheritNode in node.InheritNode.Nodes) { var value = ExecuteExpression(inheritNode, scopeContext); var BaseType = GetType(((Library.Native.System.String)value.GetProperty("TypeName")).Value, scopeContext); var instance = (SkryptType)Activator.CreateInstance(BaseType.GetType()); instance.ScopeContext = _engine.CurrentScope; instance.Engine = _engine; instance.GetPropertiesFrom(BaseType); if (value.GetType() != typeof(SkryptObject) || BaseType.GetType() != typeof(SkryptType)) { _engine.ThrowError("Can only inherit from Skrypt-based objects"); } foreach (var p in value.Properties) { var find = Object.Properties.Find(x => { if (x.IsGetter != p.IsGetter) { return(false); } if (x.IsSetter != p.IsSetter) { return(false); } if (x.Name != p.Name) { return(false); } return(true); }); if (find == null) { Object.Properties.Add(new SkryptProperty { Name = p.Name, Value = p.Value, Modifiers = p.Modifiers, IsGetter = p.IsGetter, IsSetter = p.IsSetter }); } } foreach (var p in instance.Properties) { var find = TypeObject.Properties.Find(x => { if (x.IsGetter != p.IsGetter) { return(false); } if (x.IsSetter != p.IsSetter) { return(false); } if (x.Name != p.Name) { return(false); } return(true); }); if (find == null) { TypeObject.Properties.Add(new SkryptProperty { Name = p.Name, Value = p.Value, Modifiers = p.Modifiers, IsGetter = p.IsGetter, IsSetter = p.IsSetter }); } } } foreach (var p in Object.Properties) { SetFunctionOwner(p.Value, Object); } Object.Name = node.Name; return(Object); }
/// <summary> /// Allows you to invoke a function from the engine's global scope. /// </summary> public SkryptObject Invoke(string name, params object[] arguments) { var parameters = new SkryptObject[arguments.Length]; var foundMethod = GetVariable(name, _engine.GlobalScope).Value; var input = new List <SkryptObject>(); var methodContext = new ScopeContext { ParentScope = _engine.GlobalScope }; for (int i = 0; i < arguments.Length; i++) { object arg = arguments[i]; if (arg.GetType() == typeof(int) || arg.GetType() == typeof(float) || arg.GetType() == typeof(double)) { parameters[i] = new Library.Native.System.Numeric(Convert.ToDouble(arg)); } else if (arg.GetType() == typeof(string)) { parameters[i] = new Library.Native.System.String((string)arg); } else if (arg.GetType() == typeof(bool)) { parameters[i] = new Library.Native.System.Boolean((bool)arg); } else if (arg == null) { parameters[i] = new Library.Native.System.Null(); } parameters[i].GetPropertiesFrom(GetType(((SkryptType)parameters[i]).TypeName, _engine.GlobalScope)); input.Add(parameters[i]); } ScopeContext methodScopeResult = null; if (foundMethod.GetType() == typeof(UserFunction)) { UserFunction method = (UserFunction)foundMethod; for (var i = 0; i < method.Parameters.Count; i++) { var parName = method.Parameters[i]; SkryptObject inp; inp = i < input.Count ? input[i] : new Library.Native.System.Null(); methodContext.Variables[parName] = new Variable { Name = parName, Value = inp, Scope = methodContext }; } methodScopeResult = method.Execute(_engine, null, input.ToArray(), methodContext); } else if (foundMethod.GetType() == typeof(SharpMethod)) { methodScopeResult = ((SharpMethod)foundMethod).Execute(_engine, null, input.ToArray(), methodContext); } else { _engine.ThrowError("Cannot call value, as it is not a function!"); } return(methodScopeResult.ReturnObject); }