コード例 #1
0
ファイル: Stack.cs プロジェクト: pand5461/KOS-1
        /// <summary>
        /// Call after just pushing or popping from the scope stack.  Show it the
        /// item that was popped, and if it is a trigger context, then it will adjust
        /// the triggerContextCount by the diff you give it.
        /// </summary>
        /// <param name="item">Item.</param>
        /// <param name="diff">Diff.</param>
        private void AdjustTriggerCountIfNeeded(object item, int diff)
        {
            SubroutineContext sr = item as SubroutineContext;

            if (sr != null && sr.IsTrigger)
            {
                triggerContextCount += diff;
            }
        }
コード例 #2
0
 /// <summary>
 /// Check if the call-stack has evidence that we are currently inside
 /// a trigger.
 /// </summary>
 /// <returns>True if the current call stack indicates that either we are
 /// inside a trigger, or are inside code that was in turn indirectly called
 /// from a trigger.  False if we are in mainline code instead.</returns>
 public bool HasTriggerContexts()
 {
     for (int index = stackPointer + 1; index < stack.Count; ++index)
     {
         SubroutineContext contextRecord = stack[index] as SubroutineContext;
         if (contextRecord != null)
         {
             if (contextRecord.IsTrigger)
             {
                 return(true);
             }
         }
     }
     return(false);
 }
コード例 #3
0
        public void Push(object item)
        {
            ThrowIfInvalid(item);
            stackPointer++;
            if (stack.Count < MAX_STACK_SIZE)
            {
                stack.Insert(stackPointer, ProcessItem(item));

                SubroutineContext sr = item as SubroutineContext;
                if (sr != null && sr.IsTrigger)
                {
                    ++triggerContextCount;
                }
            }
            else
            {
                // TODO: make an IKOSException for this:
                throw new Exception("Stack overflow!!");
            }
        }
コード例 #4
0
        public object Pop()
        {
            object item = null;

            if (stack.Count > 0)
            {
                item = stack[stackPointer];
                stack.RemoveAt(stackPointer);
                stackPointer--;
            }

            if (triggerContextCount > 0)
            {
                SubroutineContext sr = item as SubroutineContext;
                if (sr != null && sr.IsTrigger)
                {
                    --triggerContextCount;
                }
            }

            return(item);
        }
コード例 #5
0
ファイル: CPU.cs プロジェクト: CalebJ2/KOS
        private void ProcessTriggers()
        {
            if (currentContext.ActiveTriggerCount() <= 0) return;
            int oldCount = currentContext.Program.Count;

            int currentInstructionPointer = currentContext.InstructionPointer;
            var triggersToBeExecuted = new List<int>();

            // To ensure triggers execute in the same order in which they
            // got added (thus ensuring the system favors trying the least
            // recently added trigger first in a more fair round-robin way),
            // the nested function calls get built in stack order, so that
            // they execute in normal order.  Thus the backward iteration
            // order used in the loop below:
            for (int index = currentContext.ActiveTriggerCount() - 1 ; index >= 0 ; --index)
            {
                int triggerPointer = currentContext.GetTriggerByIndex(index);

                // If the program is ended from within a trigger, the trigger list will be empty and the pointer
                // will be invalid.  Only execute the trigger if it still exists.
                if (currentContext.ContainsTrigger(triggerPointer))
                {
                    // Insert a faked function call as if the trigger had been called from just
                    // before whatever opcode was about to get executed, by pusing a context
                    // record like OpcodeCall would do, and moving the IP to the
                    // first line of the trigger, like OpcodeCall would do.  Because
                    // triggers can't take arguments, most of the messy work of
                    // OpcodeCall.Execute isn't needed:
                    SubroutineContext contextRecord =
                        new SubroutineContext(currentInstructionPointer, triggerPointer);
                    PushAboveStack(contextRecord);
                    PushStack(new KOSArgMarkerType()); // to go with the faked function call of zero arguments.

                    triggersToBeExecuted.Add(triggerPointer);

                    currentInstructionPointer = triggerPointer;
                    // Triggers can chain in this loop if more than one fire off at once - the second trigger
                    // will look like it was a function that was called from the start of the first trigger.
                    // The third trigger will look like a function that was called from the start of the second, etc.
                }
            }

            // Remove all triggers that will fire.  Any trigger that wants to
            // re-enable itself will do so by returning a boolean true, which
            // will tell OpcodeReturn that it needs to re-add the trigger.
            foreach (int triggerPointer in triggersToBeExecuted)
                RemoveTrigger(triggerPointer);

            currentContext.InstructionPointer = currentInstructionPointer;
        }
コード例 #6
0
ファイル: Opcode.cs プロジェクト: CalebJ2/KOS
        /// <summary>
        /// Performs the actual execution of a subroutine call, either from this opcode or externally from elsewhere.
        /// All "call a routine" logic should shunt through this code here, which handles all the complex cases,
        /// or at least it should.
        /// Note that in the case of a user function, this does not *ACTUALLY* execute the function yet.  It just
        /// arranges the stack correctly for the call and returns the new location that the IP should be jumped to
        /// on the next instruction to begin the subroutine.  For all built-in cases, it actually executes the 
        /// call right now and doesn't return until it's done.  But for User functions it can't do that - it can only
        /// advise on where to jump on the next instruction to begin the function.
        /// </summary>
        /// <param name="cpu">the cpu its running on</param>
        /// <param name="direct">same meaning as OpcodeCall.Direct</param>
        /// <param name="destination">if direct, then this is the function name</param>
        /// <param name="calledFromKOSDelegateCall">true if KOSDelegate.Call() brought us here.  If true that
        /// means any pre-bound args are already on the stack.  If false it means they aren't and this will have to
        /// put them there.</param>
        /// <returns>new IP to jump to, if this should be followed up by a jump.  If -1 then it means don't jump.</returns>
        public static int StaticExecute(ICpu cpu, bool direct, object destination, bool calledFromKOSDelegateCall)
        {
            object functionPointer;
            object delegateReturn = null;
            int newIP = -1; // new instruction pointer to jump to, next, if any.

            if (direct)
            {
                functionPointer = cpu.GetValue(destination);
                if (functionPointer == null)
                    throw new KOSException("Attempt to call function failed - Value of function pointer for " + destination + " is null.");
            }
            else // for indirect calls, dig down to find what's underneath the argument list in the stack and use that:
            {
                bool foundBottom = false;
                int digDepth;
                int argsCount = 0;
                for (digDepth = 0; (! foundBottom) && digDepth < cpu.GetStackSize() ; ++digDepth)
                {
                    object arg = cpu.PeekValue(digDepth);
                    if (arg != null && arg.GetType() == ArgMarkerType)
                        foundBottom = true;
                    else
                        ++argsCount;
                }
                functionPointer = cpu.PeekValue(digDepth);
                if (! ( functionPointer is Delegate || functionPointer is KOSDelegate || functionPointer is ISuffixResult))
                {
                    // Indirect calls are meant to be delegates.  If they are not, then that means the
                    // function parentheses were put on by the user when they weren't required.  Just dig
                    // through the stack to the result of the getMember and skip the rest of the execute logic

                    // If args were passed to a non-method, then clean them off the stack, and complain:
                    if (argsCount>0)
                    {
                        for (int i=1 ; i<=argsCount; ++i)
                            cpu.PopValue();
                        throw new KOSArgumentMismatchException(
                            0, argsCount, "\n(In fact in this case the parentheses are entirely optional)");
                    }
                    cpu.PopValue(); // pop the ArgMarkerString too.
                    return -1;
                }
            }

            // If it's a string it might not really be a built-in, it might still be a user func.
            // Detect whether it's built-in, and if it's not, then convert it into the equivalent
            // user func call by making it be an integer instruction pointer instead:
            if (functionPointer is string || functionPointer is StringValue)
            {
                string functionName = functionPointer.ToString();
                if (functionName.EndsWith("()"))
                    functionName = functionName.Substring(0, functionName.Length - 2);
                if (!(cpu.BuiltInExists(functionName)))
                {
                    // It is not a built-in, so instead get its value as a user function pointer variable, despite
                    // the fact that it's being called AS IF it was direct.
                    if (!functionName.EndsWith("*")) functionName = functionName + "*";
                    if (!functionName.StartsWith("$")) functionName = "$" + functionName;
                    functionPointer = cpu.GetValue(functionName);
                }
            }

            KOSDelegate kosDelegate = functionPointer as KOSDelegate;
            if (kosDelegate != null)
            {
                if (! calledFromKOSDelegateCall)
                    kosDelegate.InsertPreBoundArgs();
            }

            IUserDelegate userDelegate = functionPointer as IUserDelegate;
            if (userDelegate != null)
                functionPointer = userDelegate.EntryPoint;

            BuiltinDelegate builtinDel = functionPointer as BuiltinDelegate;
            if (builtinDel != null && (! calledFromKOSDelegateCall) )
                functionPointer = builtinDel.Name;

            // If the IP for a jump location got encapsulated as a user int when it got stored
            // into the internal variable, then get the primitive int back out of it again:
            ScalarIntValue userInt = functionPointer as ScalarIntValue;
            if (userInt != null)
                functionPointer = userInt.GetIntValue();

            // Convert to int instead of cast in case the identifier is stored
            // as an encapsulated ScalarValue, preventing an unboxing collision.
            if (functionPointer is int || functionPointer is ScalarValue)
            {
                CpuUtility.ReverseStackArgs(cpu, direct);
                var contextRecord = new SubroutineContext(cpu.InstructionPointer+1);
                newIP = Convert.ToInt32(functionPointer);

                cpu.PushAboveStack(contextRecord);
                if (userDelegate != null)
                {
                    cpu.AssertValidDelegateCall(userDelegate);
                    // Reverse-push the closure's scope record, just after the function return context got put on the stack.
                    for (int i = userDelegate.Closure.Count - 1 ; i >= 0 ; --i)
                        cpu.PushAboveStack(userDelegate.Closure[i]);
                }
            }
            else if (functionPointer is string)
            {
                // Built-ins don't need to dereference the stack values because they
                // don't leave the scope - they're not implemented that way.  But later we
                // might want to change that.
                var name = functionPointer as string;
                string functionName = name;
                if (functionName.EndsWith("()"))
                    functionName = functionName.Substring(0, functionName.Length - 2);
                cpu.CallBuiltinFunction(functionName);

                // If this was indirect, we need to consume the thing under the return value.
                // as that was the indirect BuiltInDelegate:
                if ((! direct) && builtinDel != null)
                {
                    object topThing = cpu.PopStack();
                    cpu.PopStack(); // remove BuiltInDelegate object.
                    cpu.PushStack(topThing); // put return value back.
                }
            }
            else if (functionPointer is ISuffixResult)
            {
                var result = (ISuffixResult) functionPointer;

                if (!result.HasValue)
                {
                    result.Invoke(cpu);
                }

                delegateReturn = result.Value;
            }
            // TODO:erendrake This else if is likely never used anymore
            else if (functionPointer is Delegate)
            {
                throw new KOSYouShouldNeverSeeThisException("OpcodeCall unexpected function pointer delegate");
            }
            else
            {
                throw new KOSNotInvokableException(functionPointer);
            }

            if (functionPointer is ISuffixResult)
            {
                if (! (delegateReturn is KOSPassThruReturn))
                    cpu.PushStack(delegateReturn); // And now leave the return value on the stack to be read.
            }

            return newIP;
        }
コード例 #7
0
ファイル: Opcode.cs プロジェクト: Whitecaribou/KOS
        public override void Execute(ICpu cpu)
        {
            object functionPointer;
            object delegateReturn = null;

            if (Direct)
            {
                functionPointer = cpu.GetValue(Destination);
                if (functionPointer == null)
                    throw new KOSException("Attempt to call function failed - Value of function pointer for " + Destination + " is null.");
            }
            else // for indirect calls, dig down to find what's underneath the argument list in the stack and use that:
            {
                bool foundBottom = false;
                int digDepth;
                int argsCount = 0;
                for (digDepth = 0; (! foundBottom) && digDepth < cpu.GetStackSize() ; ++digDepth)
                {
                    object arg = cpu.PeekValue(digDepth);
                    if (arg != null && arg.GetType() == ArgMarkerType)
                        foundBottom = true;
                    else
                        ++argsCount;
                }
                functionPointer = cpu.PeekValue(digDepth);
                if (! ( functionPointer is Delegate))
                {
                    // Indirect calls are meant to be delegates.  If they are not, then that means the
                    // function parentheses were put on by the user when they weren't required.  Just dig
                    // through the stack to the result of the getMember and skip the rest of the execute logic

                    // If args were passed to a non-method, then clean them off the stack, and complain:
                    if (argsCount>0)
                    {
                        for (int i=1 ; i<=argsCount; ++i)
                            cpu.PopValue();
                        throw new KOSArgumentMismatchException(
                            0, argsCount, "\n(In fact in this case the parentheses are entirely optional)");
                    }
                    cpu.PopValue(); // pop the ArgMarkerString too.
                    return;
                }
            }
            
            // If it's a string it might not really be a built-in, it might still be a user func.
            // Detect whether it's built-in, and if it's not, then convert it into the equivalent
            // user func call by making it be an integer instruction pointer instead:
            if (functionPointer is string)
            {
                string functionName = functionPointer as string;
                if (functionName.EndsWith("()"))
                    functionName = functionName.Substring(0, functionName.Length - 2);
                if (!(cpu.BuiltInExists(functionName)))
                {
                    // It is not a built-in, so instead get its value as a user function pointer variable, despite 
                    // the fact that it's being called AS IF it was direct.
                    if (!functionName.EndsWith("*")) functionName = functionName + "*";
                    if (!functionName.StartsWith("$")) functionName = "$" + functionName;
                    functionPointer = cpu.GetValue(functionName);
                }
            }
            IUserDelegate userDelegate = functionPointer as IUserDelegate;
            if (userDelegate != null)
                functionPointer = userDelegate.EntryPoint;

            if (functionPointer is int)
            {
                ReverseStackArgs(cpu);
                int currentPointer = cpu.InstructionPointer;
                DeltaInstructionPointer = (int)functionPointer - currentPointer;
                var contextRecord = new SubroutineContext(currentPointer+1);
                cpu.PushAboveStack(contextRecord);
                if (userDelegate != null)
                {
                    cpu.AssertValidDelegateCall(userDelegate);
                    // Reverse-push the closure's scope record, just after the function return context got put on the stack.
                    for (int i = userDelegate.Closure.Count - 1 ; i >= 0 ; --i)
                        cpu.PushAboveStack(userDelegate.Closure[i]);
                }
            }
            else if (functionPointer is string)
            {
                // Built-ins don't need to dereference the stack values because they
                // don't leave the scope - they're not implemented that way.  But later we
                // might want to change that.
                var name = functionPointer as string;
                string functionName = name;
                if (functionName.EndsWith("()"))
                    functionName = functionName.Substring(0, functionName.Length - 2);
                cpu.CallBuiltinFunction(functionName);
            }
            else if (functionPointer is Delegate)
            {
                delegateReturn = ExecuteDelegate(cpu, (Delegate)functionPointer);
            }
            else
            {
                // This is one of those "the user had better NEVER see this error" sorts of messages that's here to keep us in check:
                throw new Exception(
                    string.Format("kOS internal error: OpcodeCall calling a function described using {0} which is of type {1} and kOS doesn't know how to call that.", functionPointer, functionPointer.GetType().Name)
                    );
            }

            if (! Direct)
            {
                cpu.PopValue(); // consume function name, branch index, or delegate
            }
            if (functionPointer is Delegate)
            {
                cpu.PushStack(delegateReturn); // And now leave the return value on the stack to be read.
            }
        }