Esempio n. 1
0
        protected IEnumerable <Opcode> BuildBoilerplateLoader()
        {
            List <Opcode> boilerplate = new List <Opcode>();

            InternalPath path = new BuiltInPath();

            // First label of the load/runner will be called "@LR00", which is important because that's
            // what we hardcode all the compiler-built VisitRunStatement's to call out to:
            labelCounter = 0;
            labelFormat  = "@LR{0:D2}";
            boilerplate.Add(new OpcodePushScope(-999, 0)
            {
                Label = nextLabel, SourcePath = path
            });

            // High level kerboscript function calls flip the argument orders for us, but
            // low level kRISC does not so the parameters have to be read in stack order:

            // store parameter 2 in a local name:
            boilerplate.Add(new OpcodeStoreLocal("$runonce")
            {
                Label = nextLabel, SourcePath = path
            });

            // store parameter 1 in a local name:
            boilerplate.Add(new OpcodeStoreLocal("$filename")
            {
                Label = nextLabel, SourcePath = path
            });

            // Unconditionally call load() no matter what.  load() will abort and return
            // early if the program was already compiled, and tell us that on the stack:
            boilerplate.Add(new OpcodePush(new kOS.Safe.Execution.KOSArgMarkerType())
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush("$filename")
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeEval()
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush(true)
            {
                Label = nextLabel, SourcePath = path
            });                                                                           // the flag that tells load() to abort early if it's already loaded:
            boilerplate.Add(new OpcodePush(null)
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeCall("load()")
            {
                Label = nextLabel, SourcePath = path
            });

            // Stack now has the 2 return values of load():
            //    Topmost value is a boolean flag for whether or not the program was already loaded.
            //    Second-from-top value is the entry point into the program.

            // If load() didn't claim the program was already loaded, or if we aren't operating
            // in "once" mode, then jump to the part where we call the loaded program, else
            // fall through to a dummy do-nothing return for the "run once, but it already ran" case:
            Opcode branchFromOne = new OpcodeBranchIfFalse()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchFromOne);
            boilerplate.Add(new OpcodePush("$runonce")
            {
                Label = nextLabel, SourcePath = path
            });
            Opcode branchFromTwo = new OpcodeBranchIfFalse()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchFromTwo);
            boilerplate.Add(new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            });                                                                      // onsume the entry point that load() returned. We won't be calling it.
            boilerplate.Add(new OpcodePush(0)
            {
                Label = nextLabel, SourcePath = path
            });                                                                          // ---+-- The dummy do-nothing return.
            boilerplate.Add(new OpcodeReturn(1)
            {
                Label = nextLabel, SourcePath = path
            });                                                                          // ---'

            // Actually call the Program from its entry Point, which is now the thing left on top
            // of the stack from the second return value of load():
            Opcode branchTo = new OpcodeStoreLocal("$entrypoint")
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchTo);
            boilerplate.Add(new OpcodeCall("$entrypoint")
            {
                Label = nextLabel, SourcePath = path
            });

            boilerplate.Add(new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush(0)
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeReturn(1)
            {
                Label = nextLabel, SourcePath = path
            });

            branchFromOne.DestinationLabel = branchTo.Label;
            branchFromTwo.DestinationLabel = branchTo.Label;

            return(boilerplate);
        }
Esempio n. 2
0
        private void PreProcessRunStatement(ParseNode node)
        {
            if (_options.LoadProgramsInSameAddressSpace)
            {
                bool hasON = node.Nodes.Any(cn => cn.Token.Type == TokenType.ON);
                if (!hasON)
                {
                    string subprogramName = node.Nodes[1].Token.Text;
                    if (!_context.Subprograms.Contains(subprogramName))
                    {
                        Subprogram subprogramObject = _context.Subprograms.GetSubprogram(subprogramName);
                        // Function code
                        _currentCodeSection = subprogramObject.FunctionCode;
                        // verify if the program has been loaded
                        Opcode functionStart = AddOpcode(new OpcodePush(subprogramObject.PointerIdentifier));
                        AddOpcode(new OpcodePush(0));
                        AddOpcode(new OpcodeCompareEqual());
                        OpcodeBranchIfFalse branchOpcode = new OpcodeBranchIfFalse();
                        AddOpcode(branchOpcode);
                        // if it wasn't then load it now
                        AddOpcode(new OpcodePush(subprogramObject.PointerIdentifier));
                        AddOpcode(new OpcodePush(subprogramObject.SubprogramName));
                        AddOpcode(new OpcodeCall("load()"));
                        // store the address of the program in the pointer variable
                        // (the load() function pushes the address onto the stack)
                        AddOpcode(new OpcodeStore());
                        // call the program
                        Opcode callOpcode = AddOpcode(new OpcodeCall(subprogramObject.PointerIdentifier));
                        // set the call opcode as the destination of the previous branch
                        branchOpcode.DestinationLabel = callOpcode.Label;
                        // return to the caller address
                        AddOpcode(new OpcodeReturn());
                        // set the function start label
                        subprogramObject.FunctionLabel = functionStart.Label;

                        // Initialization code
                        _currentCodeSection = subprogramObject.InitializationCode;
                        // initialize the pointer to zero
                        AddOpcode(new OpcodePush(subprogramObject.PointerIdentifier));
                        AddOpcode(new OpcodePush(0));
                        AddOpcode(new OpcodeStore());
                    }
                }
            }
        }
Esempio n. 3
0
        private void PreProcessRunStatement(ParseNode node)
        {
            NodeStartHousekeeping(node);
            if (options.LoadProgramsInSameAddressSpace)
            {
                int progNameIndex = 1;
                if (node.Nodes[1].Token.Type == TokenType.ONCE)
                {
                    ++progNameIndex;
                }
                bool hasOn = node.Nodes.Any(cn => cn.Token.Type == TokenType.ON);
                if (!hasOn)
                {
                    string subprogramName = node.Nodes[progNameIndex].Token.Text; // It assumes it already knows at compile-time how many unique program filenames exist, 
                    if (!context.Subprograms.Contains(subprogramName)) // which it uses to decide how many of these blocks to make,
                    {                                                   // which is why we can't defer run filenames until runtime like we can with the others.
                        Subprogram subprogramObject = context.Subprograms.GetSubprogram(subprogramName);
                        // Function code
                        currentCodeSection = subprogramObject.FunctionCode;
                        // verify if the program has been loaded
                        Opcode functionStart = AddOpcode(new OpcodePush(subprogramObject.PointerIdentifier));
                        // Becuse of Cpu.SaveAndClearPointers(), the subprogram's pointer identifier won't
                        // exist in this program context until it has been compiled once.  If it does exist,
                        // then skip the compiling step and just run the code that's already there:
                        AddOpcode(new OpcodeExists());
                        // branch to where the compiler loads the code:
                        OpcodeBranchIfFalse branchToLoad = new OpcodeBranchIfFalse();
                        AddOpcode(branchToLoad);
                        // Now the top of the stack should be the argument pushed by
                        // VisitRunStatement that flags if there was a 'once' keyword:
                        // If there was no 'once', then branch to where the already
                        // loaded code gets run, even though it's been run before:
                        OpcodeBranchIfFalse branchToRun = new OpcodeBranchIfFalse();
                        AddOpcode(branchToRun);
                        // If it falls through to here, that means the code was already
                        // loaded, AND the 'once' keyword was present in the instance
                        // where this loading function got called, so just do nothing
                        // and return.
                        AddOpcode(new OpcodePush(0));
                        AddOpcode(new OpcodeReturn(0));
                        // if it wasn't then load it now:
                        // First throw away the 'once' argument since it doesn't matter when
                        // we're going to be compiling regardless:
                        OpcodePop firstOpcodeOfLoadSection = new OpcodePop();
                        AddOpcode(firstOpcodeOfLoadSection);
                        branchToLoad.DestinationLabel = firstOpcodeOfLoadSection.Label;
                        AddOpcode(new OpcodePush(subprogramObject.PointerIdentifier));
                        AddOpcode(new OpcodePush(new KOSArgMarkerType()));
                        AddOpcode(new OpcodePush(subprogramObject.SubprogramName));
                        AddOpcode(new OpcodePush(null)); // The output filename - only used for compile-to-file rather than for running.
                        AddOpcode(new OpcodeCall("load()"));
                        AddOpcode(new OpcodePop()); // all functions now return a value even if it's a dummy we ignore.
                        // store the address of the program in the pointer variable
                        // (the load() function pushes the address onto the stack)
                        AddOpcode(new OpcodeStore());
                        // call the program
                        Opcode callOpcode = AddOpcode(new OpcodeCall(subprogramObject.PointerIdentifier));
                        // set the call opcode as the destination of the previous branch
                        branchToRun.DestinationLabel = callOpcode.Label;
                        // return to the caller address, after adding a dummy return val:

                        // maybe TODO?  Right now the RETURN command is being prevented from being used outside 
                        // a function declaration.  But in principle we could have programs return exit codes
                        // using the same architecture, and in fact that is why this dummy return value is needed,
                        // because OpcodeReturn now expects such a return value to exist and throws an exception when it
                        // does not.
                        // If an EXIT command was implemented, it would maybe allow an exit code that can be read here:
                        AddOpcode(new OpcodePop()); // for now: throw away return code from subprogram.
                        AddOpcode(new OpcodePush(0)); // Replace it with new dummy return code.
                        AddOpcode(new OpcodeReturn(0)); // return that.

                        // set the function start label
                        subprogramObject.FunctionLabel = functionStart.Label;

                        // Initialization code
                        currentCodeSection = subprogramObject.InitializationCode;
                        // removed pointer initialization since it overwrote any existing values when loading ksm files.
                    }
                }
            }
        }
Esempio n. 4
0
        private void PreProcessRunStatement(ParseNode node)
        {
            NodeStartHousekeeping(node);
            if (options.LoadProgramsInSameAddressSpace)
            {
                bool hasOn = node.Nodes.Any(cn => cn.Token.Type == TokenType.ON);
                if (!hasOn)
                {
                    string subprogramName = node.Nodes[1].Token.Text; // It assumes it already knows at compile-time how many unique program filenames exist,
                    if (!context.Subprograms.Contains(subprogramName)) // which it uses to decide how many of these blocks to make,
                    {                                                   // which is why we can't defer run filenames until runtime like we can with the others.
                        Subprogram subprogramObject = context.Subprograms.GetSubprogram(subprogramName);
                        // Function code
                        currentCodeSection = subprogramObject.FunctionCode;
                        // verify if the program has been loaded
                        Opcode functionStart = AddOpcode(new OpcodePush(subprogramObject.PointerIdentifier));
                        AddOpcode(new OpcodePush(-1));
                        AddOpcode(new OpcodeCompareEqual());
                        OpcodeBranchIfFalse branchOpcode = new OpcodeBranchIfFalse();
                        AddOpcode(branchOpcode);
                        // if it wasn't then load it now
                        AddOpcode(new OpcodePush(subprogramObject.PointerIdentifier));
                        AddOpcode(new OpcodePush(OpcodeCall.ARG_MARKER_STRING));
                        AddOpcode(new OpcodePush(subprogramObject.SubprogramName));
                        AddOpcode(new OpcodePush(null)); // The output filename - only used for compile-to-file rather than for running.
                        AddOpcode(new OpcodeCall("load()"));
                        AddOpcode(new OpcodePop()); // all functions now return a value even if it's a dummy we ignore.
                        // store the address of the program in the pointer variable
                        // (the load() function pushes the address onto the stack)
                        AddOpcode(new OpcodeStore());
                        // call the program
                        Opcode callOpcode = AddOpcode(new OpcodeCall(subprogramObject.PointerIdentifier));
                        // set the call opcode as the destination of the previous branch
                        branchOpcode.DestinationLabel = callOpcode.Label;
                        // return to the caller address, after adding a dummy return val:

                        // maybe TODO?  Right now the RETURN command is being prevented from being used outside
                        // a function declaration.  But in principle we could have programs return exit codes
                        // using the same architecture, and in fact that is why this dummy return value is needed,
                        // because OpcodeReturn now expects such a return value to exist and throws an exception when it
                        // does not.
                        // If an EXIT command was implemented, it would maybe allow an exit code that can be read here:
                        AddOpcode(new OpcodePop()); // for now: throw away return code from subprogram.
                        AddOpcode(new OpcodePush(0)); // Replace it with new dummy return code.
                        AddOpcode(new OpcodeReturn(0)); // return that.

                        // set the function start label
                        subprogramObject.FunctionLabel = functionStart.Label;

                        // Initialization code
                        currentCodeSection = subprogramObject.InitializationCode;
                        // initialize the pointer to zero
                        AddOpcode(new OpcodePush(subprogramObject.PointerIdentifier));
                        AddOpcode(new OpcodePush(-1));
                        AddOpcode(new OpcodeStore());
                    }
                }
            }
        }
Esempio n. 5
0
        protected IEnumerable <Opcode> BuildBoilerplateLoader()
        {
            List <Opcode> boilerplate = new List <Opcode>();

            InternalPath path = new BuiltInPath();

            // First label of the load/runner will be called "@LR00", which is important because that's
            // what we hardcode all the compiler-built VisitRunStatement's to call out to:
            labelCounter = 0;
            labelFormat  = "@LR{0:D2}";
            boilerplate.Add(new OpcodePushScope(-999, 0)
            {
                Label = nextLabel, SourcePath = path
            });

            // High level kerboscript function calls flip the argument orders for us, but
            // low level kRISC does not so the parameters have to be read in stack order:

            // store parameter 2 in a local name:
            boilerplate.Add(new OpcodeStoreLocal("$runonce")
            {
                Label = nextLabel, SourcePath = path
            });

            // store parameter 1 in a local name:
            boilerplate.Add(new OpcodeStoreLocal("$filename")
            {
                Label = nextLabel, SourcePath = path
            });

            // Unconditionally call load() no matter what.  load() will abort and return
            // early if the program was already compiled, and tell us that on the stack:
            boilerplate.Add(new OpcodePush(new kOS.Safe.Execution.KOSArgMarkerType())
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush("$filename")
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeEval()
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush(true)
            {
                Label = nextLabel, SourcePath = path
            });                                                                           // the flag that tells load() to abort early if it's already loaded:
            boilerplate.Add(new OpcodePush(null)
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeCall("load()")
            {
                Label = nextLabel, SourcePath = path
            });

            // Stack now has the 2 return values of load():
            //    Topmost value is a boolean flag for whether or not the program was already loaded.
            //    Second-from-top value is the entry point into the program.

            // If load() didn't claim the program was already loaded, or if we aren't operating
            // in "once" mode, then jump to the part where we call the loaded program, else
            // fall through to a dummy do-nothing return for the "run once, but it already ran" case:
            Opcode branchFromOne = new OpcodeBranchIfFalse()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchFromOne);
            boilerplate.Add(new OpcodePush("$runonce")
            {
                Label = nextLabel, SourcePath = path
            });
            Opcode branchFromTwo = new OpcodeBranchIfFalse()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchFromTwo);

            // If we fall through to this opcode (the br.false above doesn't jump), we are
            // in the "program already ran, so skip running it" clause of a RUN ONCE.
            // In that case the stack will still have the jump address returned by load(),
            // and also all the args that were meant to be passed to the program (which is not
            // getting run.)  In that case, we need to pop the stack of everything above the
            // next KOSArgMarker to emulate what a call would have done to the stack had it run.
            Opcode loopStart = new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(loopStart);
            boilerplate.Add(new OpcodeTestArgBottom()
            {
                Label = nextLabel, SourcePath = path
            });
            Opcode branchThatExitsLoop = new OpcodeBranchIfTrue()
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchThatExitsLoop);
            boilerplate.Add(new OpcodeBranchJump()
            {
                Label = nextLabel, SourcePath = path, DestinationLabel = loopStart.Label
            });
            Opcode afterLoop = new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            };                                                                           // Pop the KOSArgMarker that was under the args

            boilerplate.Add(afterLoop);
            branchThatExitsLoop.DestinationLabel = afterLoop.Label;
            boilerplate.Add(new OpcodePush(0)
            {
                Label = nextLabel, SourcePath = path
            });                                                                            // ---+-- The dummy do-nothing return.
            boilerplate.Add(new OpcodeReturn(1)
            {
                Label = nextLabel, SourcePath = path
            });                                                                          // ---'

            // Actually call the Program from its entry Point, which is now the thing left on top
            // of the stack from the second return value of load():
            Opcode branchTo = new OpcodeStoreLocal("$entrypoint")
            {
                Label = nextLabel, SourcePath = path
            };

            boilerplate.Add(branchTo);
            boilerplate.Add(new OpcodeCall("$entrypoint")
            {
                Label = nextLabel, SourcePath = path
            });

            boilerplate.Add(new OpcodePop()
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodePush(0)
            {
                Label = nextLabel, SourcePath = path
            });
            boilerplate.Add(new OpcodeReturn(1)
            {
                Label = nextLabel, SourcePath = path
            });

            branchFromOne.DestinationLabel = branchTo.Label;
            branchFromTwo.DestinationLabel = branchTo.Label;

            return(boilerplate);
        }
Esempio n. 6
0
        /// <summary>
        /// Process a single parameter from the parameter list for a
        /// function or program.  i.e. if encountering the statement
        /// "DECLARE PARAMETER AA, BB, CC is 0." , then this method needs to be
        /// called 3 times, once for AA, once for BB, and once for "CC is 0":
        /// </summary>
        /// <param name="whereToStore">is it local or global or lazyglobal</param>
        /// <param name="identifierNode">Parse node holding the identifier of the param</param>
        /// <param name="expressionNode">Parse node holding the expression to initialize to if
        /// this is a defaultable parameter.  If it is not a defaultable parameter, pass null here</param>
        private void VisitDeclareOneParameter(StorageModifier whereToStore, ParseNode identifierNode, ParseNode expressionNode)
        {
            if (expressionNode != null)
            {
                // This tests each defaultable parameter to see if it's at arg bottom.
                // The test must be repeated for each parameter rather than optimizing by
                // falling through to all subsequent defaulter expressions for the rest of
                // the parameters once the first one finds arg bottom.
                // This is because kerboscript does not require the declare parameters to
                // be contiguous statements so there may be code in between them you're
                // not supposed to skip over.

                AddOpcode(new OpcodeTestArgBottom());
                OpcodeBranchIfFalse branchSkippingInit = new OpcodeBranchIfFalse();
                AddOpcode(branchSkippingInit);

                VisitNode(expressionNode); // evals init expression on the top of the stack where the arg would have been

                branchSkippingInit.DestinationLabel = GetNextLabel(false);
            }
            VisitNode(identifierNode);
            AddOpcode(new OpcodeSwap());
            AddOpcode(CreateAppropriateStoreCode(whereToStore, true));
        }
Esempio n. 7
0
        private void PreProcessOnStatement(ParseNode node)
        {
            NodeStartHousekeeping(node);
            nextBraceIsFunction = true; // triggers aren't really functions but act like it a lot.

            int expressionHash = ConcatenateNodes(node).GetHashCode();
            string triggerIdentifier = "on-" + expressionHash.ToString();
            Trigger triggerObject = context.Triggers.GetTrigger(triggerIdentifier);

            currentCodeSection = triggerObject.Code;
            // Put the old value on top of the stack for equals comparison later:
            AddOpcode(new OpcodePush(triggerObject.OldValueIdentifier));
            AddOpcode(new OpcodeEval());
            // eval the expression for the new value, and leave it on the stack twice.
            VisitNode(node.Nodes[1]);
            AddOpcode(new OpcodeEval());
            AddOpcode(new OpcodeDup());
            // Put one of those two copies of the new value into the old value identifier for next time:
            AddOpcode(new OpcodePush(triggerObject.OldValueIdentifier));
            AddOpcode(new OpcodeSwap());
            AddOpcode(new OpcodeStoreGlobal());
            // Use the other dup'ed copy of the new value to actually do the equals
            // comparison with the old value that's still under it on the stack:
            AddOpcode(new OpcodeCompareEqual());
            OpcodeBranchIfFalse branchToBody = new OpcodeBranchIfFalse();
            branchToBody.Distance = 3;
            AddOpcode(branchToBody);
            AddOpcode(new OpcodePush(true));       // wasn't triggered yet, so preserve.
            AddOpcode(new OpcodeReturn((short)0)); // premature return because it wasn't triggered

            // make flag that remembers whether to remove trigger:
            // defaults to true = removal should happen.
            string triggerKeepName = "$keep-" + triggerIdentifier;
            PushTriggerKeepName(triggerKeepName);
            AddOpcode(new OpcodePush(triggerKeepName));
            AddOpcode(new OpcodePush(false));
            AddOpcode(new OpcodeStoreGlobal());

            VisitNode(node.Nodes[2]);

            // PRESERVE will determine whether or not the trigger returns true (true means
            // re-enable the trigger upon exit.)
            PopTriggerKeepName();
            AddOpcode(new OpcodePush(triggerKeepName));
            AddOpcode(new OpcodeReturn((short)0));

            nextBraceIsFunction = false;
        }