diff --git a/src/kOS.CommandLine/Binding/BindingConfig.cs b/src/kOS.CommandLine/Binding/BindingConfig.cs new file mode 100644 index 000000000..1bc368828 --- /dev/null +++ b/src/kOS.CommandLine/Binding/BindingConfig.cs @@ -0,0 +1,15 @@ +using kOS.Safe.Binding; +using kOS.Safe.Utilities; +using kOS.Safe; + +namespace kOS.CommandLine.Binding +{ + [Binding("ksp")] + public class BindingConfig : kOS.Safe.Binding.SafeBinding + { + public override void AddTo(SharedObjects shared) + { + shared.BindingMgr.AddGetter("CONFIG", () => SafeHouse.Config); + } + } +} diff --git a/src/kOS.CommandLine/Binding/BindingManager.cs b/src/kOS.CommandLine/Binding/BindingManager.cs new file mode 100644 index 000000000..a6df187a9 --- /dev/null +++ b/src/kOS.CommandLine/Binding/BindingManager.cs @@ -0,0 +1,158 @@ +using kOS.Safe; +using kOS.Safe.Binding; +using kOS.Safe.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace kOS.CommandLine.Binding +{ + [AssemblyWalk(AttributeType = typeof(BindingAttribute), StaticRegisterMethod = "RegisterMethod")] + public class BindingManager : IDisposable, IBindingManager + { + private readonly SharedObjects shared; + private readonly List bindings = new List(); + private readonly Dictionary variables; + private static readonly Dictionary rawAttributes = new Dictionary(); + //private FlightControlManager flightControl; + + public BindingManager(SharedObjects shared) + { + variables = new Dictionary(StringComparer.OrdinalIgnoreCase); + this.shared = shared; + this.shared.BindingMgr = this; + } + + public void Load() + { + var contexts = new string[1]; + contexts[0] = "ksp"; + + bindings.Clear(); + variables.Clear(); + //flightControl = null; + + foreach (BindingAttribute attr in rawAttributes.Keys) + { + var t = rawAttributes[attr]; + if (attr.Contexts.Any() && !attr.Contexts.Intersect(contexts).Any()) continue; + var b = (kOS.Safe.Binding.SafeBinding)Activator.CreateInstance(t); + b.AddTo(shared); + bindings.Add(b); + + //var manager = b as FlightControlManager; + //if (manager != null) + //{ + // flightControl = manager; + //} + } + } + + public static void RegisterMethod(BindingAttribute attr, Type type) + { + if (!rawAttributes.ContainsKey(attr)) + { + rawAttributes.Add(attr, type); + } + } + + public void AddBoundVariable(string name, BindingGetDlg getDelegate, BindingSetDlg setDelegate) + { + BoundVariable variable; + if (variables.ContainsKey(name)) + { + variable = variables[name]; + } + else + { + variable = new BoundVariable + { + Name = name, + }; + variables.Add(name, variable); + shared.Cpu.AddVariable(variable, name, false); + } + + if (getDelegate != null) + variable.Get = getDelegate; + + if (setDelegate != null) + variable.Set = setDelegate; + } + + public void AddGetter(string name, BindingGetDlg dlg) + { + AddBoundVariable(name, dlg, null); + } + + public void AddGetter(IEnumerable names, BindingGetDlg dlg) + { + foreach (var name in names) + { + AddBoundVariable(name, dlg, null); + } + } + + public void AddSetter(string name, BindingSetDlg dlg) + { + AddBoundVariable(name, null, dlg); + } + + public void AddSetter(IEnumerable names, BindingSetDlg dlg) + { + foreach (var name in names) + { + AddBoundVariable(name, null, dlg); + } + } + + public void PreUpdate() + { + foreach (var variable in variables) + { + variable.Value.ClearCache(); + } + // update the bindings + foreach (var b in bindings) + { + b.Update(); + } + } + + public void PostUpdate() + { + } + + public void ToggleFlyByWire(string paramName, bool enabled) + { + //if (flightControl != null) + //{ + // flightControl.ToggleFlyByWire(paramName, enabled); + //} + } + + public void UnBindAll() + { + //if (flightControl != null) + //{ + // flightControl.UnBind(); + //} + } + + public void Dispose() + { + //if (flightControl != null) + //{ + // flightControl.Dispose(); + //} + } + + public void SelectAutopilotMode(string autopilotMode) + { + //if (flightControl != null) + //{ + // flightControl.SelectAutopilotMode(autopilotMode); + //} + } + } +} \ No newline at end of file diff --git a/src/kOS.CommandLine/ConsoleLogger.cs b/src/kOS.CommandLine/ConsoleLogger.cs new file mode 100644 index 000000000..2d0d77bd6 --- /dev/null +++ b/src/kOS.CommandLine/ConsoleLogger.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text; +using kOS.Safe; + +namespace kOS.CommandLine +{ + public class ConsoleLogger : ILogger + { + public void Log(string text) + { + Console.WriteLine(text); + //throw new NotImplementedException(); + } + + public void Log(Exception e) + { + Console.WriteLine(e.Message); + //throw new NotImplementedException(); + } + + public void SuperVerbose(string s) + { + Console.WriteLine(s); + //throw new NotImplementedException(); + } + + public void LogWarning(string s) + { + Console.WriteLine(s); + //throw new NotImplementedException(); + } + + public void LogException(Exception exception) + { + Console.WriteLine(exception.Message); + //throw new NotImplementedException(); + } + + public void LogError(string s) + { + Console.WriteLine(s); + //throw new NotImplementedException(); + } + } +} diff --git a/src/kOS.CommandLine/Encapsulation/Config.cs b/src/kOS.CommandLine/Encapsulation/Config.cs new file mode 100644 index 000000000..2e3959c97 --- /dev/null +++ b/src/kOS.CommandLine/Encapsulation/Config.cs @@ -0,0 +1,206 @@ +using kOS.Safe.Encapsulation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using kOS.CommandLine.Properties; + +namespace kOS.CommandLine.Encapsulation +{ + public class Config : Structure, IConfig + { + private static Config fetch; + public static Config Fetch + { + get + { + if (fetch == null) fetch = new Config(); + return fetch; + } + } + + public int InstructionsPerUpdate + { + get + { + return 2000; + return Settings.Default.ipu; + } + set + { + Settings.Default.ipu = value; + } + } + + public bool UseCompressedPersistence + { + get + { + return false; + } + set + { + throw new NotImplementedException(); + } + } + + public bool ShowStatistics + { + get + { + return true; + } + set + { + throw new NotImplementedException(); + } + } + + public bool EnableRTIntegration + { + get + { + return false; + } + set + { + throw new NotImplementedException(); + } + } + + public bool StartOnArchive + { + get + { + return true; + } + set + { + throw new NotImplementedException(); + } + } + + public bool ObeyHideUI + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public bool EnableSafeMode + { + get + { + return true; + } + set + { + throw new NotImplementedException(); + } + } + + public bool VerboseExceptions + { + get + { + return true; + } + set + { + throw new NotImplementedException(); + } + } + + public bool EnableTelnet + { + get + { + return true; + } + set + { + throw new NotImplementedException(); + } + } + + public int TelnetPort + { + get + { + return 5410; + } + set + { + throw new NotImplementedException(); + } + } + + public bool AudibleExceptions + { + get + { + return false; + } + set + { + throw new NotImplementedException(); + } + } + + public bool TelnetLoopback + { + get + { + return true; + } + set + { + throw new NotImplementedException(); + } + } + + public bool UseBlizzyToolbarOnly + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public DateTime TimeStamp + { + get { throw new NotImplementedException(); } + } + + public bool DebugEachOpcode + { + get + { + return false; + } + set + { + throw new NotImplementedException(); + } + } + + public void SaveConfig() + { + Settings.Default.Save(); + } + + public IList GetConfigKeys() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/kOS.CommandLine/Execution/kOSWorker.cs b/src/kOS.CommandLine/Execution/kOSWorker.cs new file mode 100644 index 000000000..df5a775d9 --- /dev/null +++ b/src/kOS.CommandLine/Execution/kOSWorker.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using kOS.CommandLine.Binding; +using kOS.Safe; +using kOS.Safe.Function; +using kOS.Safe.Execution; +using kOS.Safe.Binding; +using kOS.Safe.Compilation; +using kOS.Safe.Module; +using kOS.Safe.Compilation.KS; +using kOS.Safe.Persistence; +using kOS.Safe.Utilities; + +namespace kOS.CommandLine.Execution +{ + public class kOSWorker : IProcessor + { + private SharedObjects shared; + public SharedObjects Shared { get { return shared; } } + + ProcessorModes mode = ProcessorModes.READY; + + public kOSWorker() + { + SafeHouse.Init(Encapsulation.Config.Fetch, null, null, true, Path.Combine(Directory.GetCurrentDirectory(), "Scripts")); + SafeHouse.Logger = new ConsoleLogger(); + //SafeHouse.ArchiveFolder = System.IO.Directory.GetCurrentDirectory(); + Init(); + } + + public void Init() + { + Opcode.InitMachineCodeData(); + CompiledObject.InitTypeData(); + AssemblyWalkAttribute.Walk(); + + shared = new SharedObjects(); + shared.Processor = this; + shared.UpdateHandler = new UpdateHandler(); + shared.BindingMgr = new BindingManager(shared); + //shared.Interpreter = new Screen.Interpreter(); + shared.Screen = shared.Interpreter; + shared.ScriptHandler = new KSScript(); + shared.Logger = SafeHouse.Logger; + shared.VolumeMgr = new VolumeManager(); + shared.FunctionManager = new FunctionManager(shared); + shared.Cpu = new CPU(shared); + //shared.SoundMaker = Sound.SoundMaker.Instance; + + // add archive. + var archive = new Archive(); + shared.VolumeMgr.Add(archive); + shared.VolumeMgr.SwitchTo(archive); + + shared.Cpu.Boot(); + } + + public void PushScript(string content) + { + if (shared != null && shared.ScriptHandler != null) + { + string filepath = ""; + var options = new CompilerOptions + { + LoadProgramsInSameAddressSpace = false, + FuncManager = shared.FunctionManager, + IsCalledFromRun = false + }; + //var program = shared.Cpu.SwitchToProgramContext(); + var program = shared.Cpu.GetInterpreterContext(); + var parts = shared.ScriptHandler.Compile(filepath, 0, content, "interpreter", options); + var instruction = program.AddObjectParts(parts); + shared.Cpu.InstructionPointer++; + } + } + + public void FixedUpdate() + { + shared.UpdateHandler.UpdateFixedObservers(0.04); + } + + public void SetMode(ProcessorModes newProcessorMode) + { + mode = newProcessorMode; + } + + public ProcessorModes GetMode() + { + return mode; + } + + public string BootFilename + { + get + { + // TODO: implement using a boot file. + return "None"; + } + set + { + throw new NotImplementedException(); + } + } + + public bool CheckCanBoot() + { + // TODO: Check if we can boot. + return true; + } + } +} diff --git a/src/kOS.CommandLine/Program.cs b/src/kOS.CommandLine/Program.cs new file mode 100644 index 000000000..ac1d35e9a --- /dev/null +++ b/src/kOS.CommandLine/Program.cs @@ -0,0 +1,45 @@ +using kOS.Safe.Execution; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace kOS.CommandLine +{ + class Program + { + static string run = "run perf_scalaradd3.ks. log profileresult() to \"profile-{0}.csv\". shutdown."; + static string scalarAddBenchmark = + @"set x to 1. + set y to 2. + set z to 0. + print x + y. + local count is 2000. + until count = 0 { + set z to z + x + y. + set count to count - 1. + } + shutdown."; + static string simpleTest = "set x to 0."; + static void Main(string[] args) + { + Console.WindowHeight = 50; + Console.WindowWidth = 160; + System.Threading.Thread.Sleep(2000); + var worker = new Execution.kOSWorker(); + worker.FixedUpdate(); + //worker.PushScript(simpleTest); + //worker.PushScript(scalarAddBenchmark); + worker.PushScript(string.Format(run, DateTime.Now.ToString("yyyy-MM-dd-HH-mm"))); + while (worker.GetMode() == Safe.Module.ProcessorModes.READY) + { + worker.FixedUpdate(); + System.Threading.Thread.Sleep(20); + } + //var result = worker.Shared.Cpu.ProfileResult; + //if (result != null) Console.WriteLine(string.Join("\r\n", result.ToArray())); + //Console.WriteLine("Press any key to exit..."); + //Console.ReadKey(); + } + } +} diff --git a/src/kOS.CommandLine/Properties/AssemblyInfo.cs b/src/kOS.CommandLine/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d98872aa2 --- /dev/null +++ b/src/kOS.CommandLine/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("kOS.CommandLine")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("kOS.CommandLine")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("08748f03-254e-48b3-9453-94469138408a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/kOS.CommandLine/Properties/Settings.Designer.cs b/src/kOS.CommandLine/Properties/Settings.Designer.cs new file mode 100644 index 000000000..e5773d5b0 --- /dev/null +++ b/src/kOS.CommandLine/Properties/Settings.Designer.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace kOS.CommandLine.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool stat { + get { + return ((bool)(this["stat"])); + } + set { + this["stat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool arch { + get { + return ((bool)(this["arch"])); + } + set { + this["arch"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("200")] + public int ipu { + get { + return ((int)(this["ipu"])); + } + set { + this["ipu"] = value; + } + } + } +} diff --git a/src/kOS.CommandLine/Properties/Settings.settings b/src/kOS.CommandLine/Properties/Settings.settings new file mode 100644 index 000000000..0653048c6 --- /dev/null +++ b/src/kOS.CommandLine/Properties/Settings.settings @@ -0,0 +1,15 @@ + + + + + + False + + + False + + + 200 + + + \ No newline at end of file diff --git a/src/kOS.CommandLine/Screen/Interpreter.cs b/src/kOS.CommandLine/Screen/Interpreter.cs new file mode 100644 index 000000000..39a9bc249 --- /dev/null +++ b/src/kOS.CommandLine/Screen/Interpreter.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using kOS.Safe.Screen; + +namespace kOS.CommandLine.Screen +{ + class Interpreter : IInterpreter + { + public void Type(char ch) + { + throw new NotImplementedException(); + } + + public bool SpecialKey(char key) + { + throw new NotImplementedException(); + } + + public string GetCommandHistoryAbsolute(int absoluteIndex) + { + throw new NotImplementedException(); + } + + public void SetInputLock(bool isLocked) + { + throw new NotImplementedException(); + } + + public bool IsAtStartOfCommand() + { + throw new NotImplementedException(); + } + + public void Reset() + { + throw new NotImplementedException(); + } + + public int CharacterPixelWidth + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public int CharacterPixelHeight + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public float Brightness + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public int CursorRowShow + { + get { throw new NotImplementedException(); } + } + + public int CursorColumnShow + { + get { throw new NotImplementedException(); } + } + + public int RowCount + { + get { throw new NotImplementedException(); } + } + + public int ColumnCount + { + get { throw new NotImplementedException(); } + } + + public int AbsoluteCursorRow + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public int BeepsPending + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public bool ReverseScreen + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public bool VisualBeep + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public int TopRow + { + get { throw new NotImplementedException(); } + } + + public void SetSize(int rowCount, int columnCount) + { + throw new NotImplementedException(); + } + + public int ScrollVertical(int deltaRows) + { + throw new NotImplementedException(); + } + + public void MoveCursor(int row, int column) + { + throw new NotImplementedException(); + } + + public void MoveToNextLine() + { + throw new NotImplementedException(); + } + + public void PrintAt(string textToPrint, int row, int column) + { + throw new NotImplementedException(); + } + + public void Print(string textToPrint) + { + throw new NotImplementedException(); + } + + public void Print(string textToPrint, bool addNewLine) + { + throw new NotImplementedException(); + } + + public void ClearScreen() + { + throw new NotImplementedException(); + } + + public void AddSubBuffer(SubBuffer subBuffer) + { + throw new NotImplementedException(); + } + + public void RemoveSubBuffer(SubBuffer subBuffer) + { + throw new NotImplementedException(); + } + + public List GetBuffer() + { + throw new NotImplementedException(); + } + + public void AddResizeNotifier(ScreenBuffer.ResizeNotifier notifier) + { + throw new NotImplementedException(); + } + + public void RemoveResizeNotifier(ScreenBuffer.ResizeNotifier notifier) + { + throw new NotImplementedException(); + } + + public string DebugDump() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/kOS.CommandLine/app.config b/src/kOS.CommandLine/app.config new file mode 100644 index 000000000..4b41b5fb5 --- /dev/null +++ b/src/kOS.CommandLine/app.config @@ -0,0 +1,21 @@ + + + + +
+ + + + + + False + + + False + + + 200 + + + + diff --git a/src/kOS.CommandLine/kOS.CommandLine.csproj b/src/kOS.CommandLine/kOS.CommandLine.csproj new file mode 100644 index 000000000..a3aef7518 --- /dev/null +++ b/src/kOS.CommandLine/kOS.CommandLine.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {8AB664CD-E0AD-4D8F-B654-1D084E840703} + Exe + Properties + kOS.CommandLine + kOS.CommandLine + v3.5 + 512 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + {590ffda8-7b44-4bc3-a12a-5ffe2bb7d8fd} + kOS.Safe + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + \ No newline at end of file diff --git a/src/kOS.Safe.Test/Opcode/FakeCpu.cs b/src/kOS.Safe.Test/Opcode/FakeCpu.cs index 09cd3159d..c4fb55ec9 100644 --- a/src/kOS.Safe.Test/Opcode/FakeCpu.cs +++ b/src/kOS.Safe.Test/Opcode/FakeCpu.cs @@ -249,5 +249,22 @@ public void StopCompileStopwatch() { throw new NotImplementedException(); } + + + public IProgramContext GetInterpreterContext() + { + throw new NotImplementedException(); + } + + public IProgramContext SwitchToProgramContext() + { + throw new NotImplementedException(); + } + + + public Compilation.Opcode GetCurrentOpcode() + { + throw new NotImplementedException(); + } } } diff --git a/src/kOS.Safe/Binding/SafeBinding.cs b/src/kOS.Safe/Binding/SafeBinding.cs new file mode 100644 index 000000000..2dc3917bf --- /dev/null +++ b/src/kOS.Safe/Binding/SafeBinding.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using kOS.Safe; + +namespace kOS.Safe.Binding +{ + public abstract class SafeBinding + { + public abstract void AddTo(SharedObjects shared); + + public virtual void Update() + { + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Compilation/Opcode.cs b/src/kOS.Safe/Compilation/Opcode.cs index 0dc31d479..3afda4af0 100644 --- a/src/kOS.Safe/Compilation/Opcode.cs +++ b/src/kOS.Safe/Compilation/Opcode.cs @@ -381,8 +381,10 @@ private static int MLFieldComparator(MLArgInfo a1, MLArgInfo a2) // // Reflection: A good way to make a simple idea look messier than it really is. // - var attributes1 = new List(a1.PropertyInfo.GetCustomAttributes(true) as Attribute[]); - var attributes2 = new List(a2.PropertyInfo.GetCustomAttributes(true) as Attribute[]); + var a1array = a1.PropertyInfo.GetCustomAttributes(true).Cast(); + var a2array = a2.PropertyInfo.GetCustomAttributes(true).Cast(); + var attributes1 = a1.PropertyInfo.GetCustomAttributes(true).Cast().ToList(); + var attributes2 = a2.PropertyInfo.GetCustomAttributes(true).Cast().ToList(); var f1 = (MLField) attributes1.First(a => a is MLField); var f2 = (MLField) attributes2.First(a => a is MLField); return (f1.Ordering < f2.Ordering) ? -1 : (f1.Ordering > f2.Ordering) ? 1 : 0; diff --git a/src/kOS.Safe/Execution/CPU.cs b/src/kOS.Safe/Execution/CPU.cs index 3cde01616..234b98aed 100644 --- a/src/kOS.Safe/Execution/CPU.cs +++ b/src/kOS.Safe/Execution/CPU.cs @@ -59,7 +59,7 @@ public int InstructionPointer } public double SessionTime { get { return currentTime; } } - + public List ProfileResult { get; private set; } public CPU(SharedObjects shared) @@ -158,14 +158,17 @@ private void PushInterpreterContext() private void PushContext(ProgramContext context) { - SafeHouse.Logger.Log("Pushing context staring with: " + context.GetCodeFragment(0).FirstOrDefault()); + var sb = new StringBuilder(); + sb.AppendLine("Pushing context staring with:"); + sb.AppendLine(context.GetCodeFragment(0).FirstOrDefault()); + SafeHouse.Logger.Log(sb.ToString()); SaveAndClearPointers(); contexts.Add(context); currentContext = contexts.Last(); if (contexts.Count > 1) { - shared.Interpreter.SetInputLock(true); + if (shared.Interpreter != null) shared.Interpreter.SetInputLock(true); } } @@ -194,7 +197,7 @@ private void PopContext() if (contexts.Count == 1) { - shared.Interpreter.SetInputLock(false); + if (shared.Interpreter != null) shared.Interpreter.SetInputLock(false); } } } @@ -379,8 +382,8 @@ public void BreakExecution(bool manual) PopContext(); if (contexts.Count == 1 && !silent) { - shared.Screen.Print("Program ended."); - shared.BindingMgr.UnBindAll(); + if (shared.Screen != null) shared.Screen.Print("Program ended."); + if (shared.BindingMgr != null) shared.BindingMgr.UnBindAll(); PrintStatistics(); } } @@ -1197,13 +1200,13 @@ private void ContinueExecution(bool doProfiling) } private bool ExecuteInstruction(IProgramContext context, bool doProfiling) - { + { Opcode opcode = context.Program[context.InstructionPointer]; if (SafeHouse.Config.DebugEachOpcode) { - executeLog.Append(string.Format("Executing Opcode {0:0000}/{1:0000} {2} {3}\n", context.InstructionPointer, context.Program.Count, opcode.Label, opcode)); - executeLog.Append(string.Format("Prior to exeucting, stack looks like this:\n{0}\n", DumpStack())); + executeLog.AppendLine(string.Format("Executing Opcode {0:0000}/{1:0000} {2} {3}", context.InstructionPointer, context.Program.Count, opcode.Label, opcode)); + executeLog.AppendLine(string.Format("Prior to exeucting, stack looks like this:\n{0}", DumpStack())); } try { @@ -1218,12 +1221,12 @@ private bool ExecuteInstruction(IProgramContext context, bool doProfiling) instructionWatch.Stop(); opcode.ProfileTicksElapsed += instructionWatch.ElapsedTicks; opcode.ProfileExecutionCount++; - + // start the *next* instruction's timer right after this instruction ended instructionWatch.Reset(); instructionWatch.Start(); } - + if (opcode.AbortProgram) { BreakExecution(false); @@ -1299,11 +1302,11 @@ public List GetCodeFragment(int contextLines) public string StatisticsDump(bool doProfiling) { if (!SafeHouse.Config.ShowStatistics) return ""; - + string delimiter = ""; if (doProfiling) delimiter = ","; - + StringBuilder sb = new StringBuilder(); sb.Append(string.Format("{0}{0}{0}{0}Total compile time: {0}{1:F3}ms\n", delimiter, totalCompileTime)); @@ -1320,7 +1323,7 @@ public string StatisticsDump(bool doProfiling) sb.Append(" \n"); return sb.ToString(); } - + public void ResetStatistics() { totalCompileTime = 0D; @@ -1333,13 +1336,14 @@ public void ResetStatistics() maxMainlineInstructionsSoFar = 0; maxTriggerInstructionsSoFar = 0; } - + private void PrintStatistics() { - shared.Screen.Print(StatisticsDump(false)); + if (shared.Screen != null) shared.Screen.Print(StatisticsDump(false)); + else if (shared.Logger != null) shared.Logger.Log(StatisticsDump(false)); ResetStatistics(); } - + private void CalculateProfileResult() { ProfileResult = currentContext.GetCodeFragment(0, currentContext.Program.Count - 1, true); @@ -1363,4 +1367,4 @@ public void StopCompileStopwatch() compileWatch.Stop(); } } -} \ No newline at end of file +} diff --git a/src/kOS.Safe/Execution/ICpu.cs b/src/kOS.Safe/Execution/ICpu.cs index a2feb56ef..69c82c0c1 100644 --- a/src/kOS.Safe/Execution/ICpu.cs +++ b/src/kOS.Safe/Execution/ICpu.cs @@ -48,6 +48,9 @@ public interface ICpu : IFixedUpdateObserver int InstructionsThisUpdate { get; } void StartCompileStopwatch(); void StopCompileStopwatch(); + IProgramContext GetInterpreterContext(); + IProgramContext SwitchToProgramContext(); + Opcode GetCurrentOpcode(); /// /// Return the subroutine call trace of how the code got to where it is right now. diff --git a/src/kOS.Safe/Function/Collections/FunctionLexicon.cs b/src/kOS.Safe/Function/Collections/FunctionLexicon.cs new file mode 100644 index 000000000..c317feca5 --- /dev/null +++ b/src/kOS.Safe/Function/Collections/FunctionLexicon.cs @@ -0,0 +1,19 @@ +using kOS.Safe.Encapsulation; +using System.Linq; + +namespace kOS.Safe.Function.Collections +{ + [Function("lex", "lexicon")] + public class FunctionLexicon : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + Structure[] argArray = new Structure[CountRemainingArgs(shared)]; + for (int i = argArray.Length - 1; i >= 0; --i) + argArray[i] = PopStructureAssertEncapsulated(shared); // fill array in reverse order because .. stack args. + AssertArgBottomAndConsume(shared); + var lexicon = new Lexicon(argArray.ToList()); + ReturnValue = lexicon; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Collections/FunctionList.cs b/src/kOS.Safe/Function/Collections/FunctionList.cs new file mode 100644 index 000000000..aedb37d04 --- /dev/null +++ b/src/kOS.Safe/Function/Collections/FunctionList.cs @@ -0,0 +1,19 @@ +using kOS.Safe.Encapsulation; +using System.Linq; + +namespace kOS.Safe.Function.Collections +{ + [Function("list")] + public class FunctionList : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + Structure[] argArray = new Structure[CountRemainingArgs(shared)]; + for (int i = argArray.Length - 1; i >= 0; --i) + argArray[i] = PopStructureAssertEncapsulated(shared); // fill array in reverse order because .. stack args. + AssertArgBottomAndConsume(shared); + var listValue = new ListValue(argArray.ToList()); + ReturnValue = listValue; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Collections/FunctionQueue.cs b/src/kOS.Safe/Function/Collections/FunctionQueue.cs new file mode 100644 index 000000000..514948261 --- /dev/null +++ b/src/kOS.Safe/Function/Collections/FunctionQueue.cs @@ -0,0 +1,19 @@ +using kOS.Safe.Encapsulation; +using System.Linq; + +namespace kOS.Safe.Function.Collections +{ + [Function("queue")] + public class FunctionQueue : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + Structure[] argArray = new Structure[CountRemainingArgs(shared)]; + for (int i = argArray.Length - 1; i >= 0; --i) + argArray[i] = PopStructureAssertEncapsulated(shared); // fill array in reverse order because .. stack args. + AssertArgBottomAndConsume(shared); + var queueValue = new QueueValue(argArray.ToList()); + ReturnValue = queueValue; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Collections/FunctionRange.cs b/src/kOS.Safe/Function/Collections/FunctionRange.cs new file mode 100644 index 000000000..23b82be33 --- /dev/null +++ b/src/kOS.Safe/Function/Collections/FunctionRange.cs @@ -0,0 +1,42 @@ +using kOS.Safe.Exceptions; + +namespace kOS.Safe.Function.Collections +{ + [Function("range")] + public class FunctionRange : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + // Default values for parameters + int from = RangeValue.DEFAULT_START; + int to = RangeValue.DEFAULT_STOP; + int step = RangeValue.DEFAULT_STEP; + + int argCount = CountRemainingArgs(shared); + // assign parameter values from the stack, pop them in reverse order + switch (argCount) + { + case 1: + to = GetInt(PopStructureAssertEncapsulated(shared)); + break; + + case 2: + to = GetInt(PopStructureAssertEncapsulated(shared)); + from = GetInt(PopStructureAssertEncapsulated(shared)); + break; + + case 3: + step = GetInt(PopStructureAssertEncapsulated(shared)); + to = GetInt(PopStructureAssertEncapsulated(shared)); + from = GetInt(PopStructureAssertEncapsulated(shared)); + break; + + default: + throw new KOSArgumentMismatchException(new int[] { 1, 2, 3 }, argCount, "Thrown from function RANGE()"); + } + AssertArgBottomAndConsume(shared); + + ReturnValue = new RangeValue(from, to, step); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Collections/FunctionStack.cs b/src/kOS.Safe/Function/Collections/FunctionStack.cs new file mode 100644 index 000000000..c499ff1fc --- /dev/null +++ b/src/kOS.Safe/Function/Collections/FunctionStack.cs @@ -0,0 +1,19 @@ +using kOS.Safe.Encapsulation; +using System.Linq; + +namespace kOS.Safe.Function.Collections +{ + [Function("stack")] + public class FunctionStack : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + Structure[] argArray = new Structure[CountRemainingArgs(shared)]; + for (int i = argArray.Length - 1; i >= 0; --i) + argArray[i] = PopStructureAssertEncapsulated(shared); // fill array in reverse order because .. stack args. + AssertArgBottomAndConsume(shared); + var stackValue = new StackValue(argArray.ToList()); + ReturnValue = stackValue; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/FunctionManager.cs b/src/kOS.Safe/Function/FunctionManager.cs new file mode 100644 index 000000000..6a5821440 --- /dev/null +++ b/src/kOS.Safe/Function/FunctionManager.cs @@ -0,0 +1,98 @@ +using kOS.Safe.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace kOS.Safe.Function +{ + [AssemblyWalk(AttributeType = typeof(FunctionAttribute), StaticRegisterMethod = "RegisterMethod")] + public class FunctionManager : IFunctionManager + { + private readonly SharedObjects shared; + private Dictionary functions; + private static readonly Dictionary rawAttributes = new Dictionary(); + + public FunctionManager(SharedObjects shared) + { + this.shared = shared; + Load(); + } + + public void Load() + { + functions = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (FunctionAttribute attr in rawAttributes.Keys) + { + var type = rawAttributes[attr]; + if (attr == null || type == null) continue; + object functionObject = Activator.CreateInstance(type); + foreach (string functionName in attr.Names) + { + if (functionName != string.Empty) + { + functions.Add(functionName, (SafeFunctionBase)functionObject); + } + } + } + } + + public static void WalkAssemblies() + { + rawAttributes.Clear(); + foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (!assembly.GlobalAssemblyCache) + { + WalkAssembly(assembly); + } + } + } + + public static void WalkAssembly(Assembly assembly) + { + foreach (Type type in assembly.GetTypes()) + { + var attr = (FunctionAttribute)type.GetCustomAttributes(typeof(FunctionAttribute), true).FirstOrDefault(); + if (attr == null) continue; + if (!rawAttributes.ContainsKey(attr)) + { + rawAttributes.Add(attr, type); + } + } + } + + public static void RegisterMethod(Attribute attr, Type type) + { + var funcAttr = attr as FunctionAttribute; + if (funcAttr != null && !rawAttributes.ContainsKey((funcAttr))) + { + rawAttributes.Add(funcAttr, type); + } + } + + public void CallFunction(string functionName) + { + if (!functions.ContainsKey(functionName)) + { + throw new Exception("Call to non-existent function " + functionName); + } + + SafeFunctionBase function = functions[functionName]; + function.Execute(shared); + if (function.UsesAutoReturn) + shared.Cpu.PushStack(function.ReturnValue); + } + + /// + /// Find out if the function with the name given exists already in the built-in hardcoded functions set + /// (as opposed to the user functions). + /// + /// check if this function exists + /// true if it does exist (as a built-in, not as a user function) + public bool Exists(string functionName) + { + return functions.ContainsKey(functionName); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionAbs.cs b/src/kOS.Safe/Function/KOSMath/FunctionAbs.cs new file mode 100644 index 000000000..f01017c8e --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionAbs.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("abs")] + public class FunctionAbs : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = Math.Abs(argument); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionCeiling.cs b/src/kOS.Safe/Function/KOSMath/FunctionCeiling.cs new file mode 100644 index 000000000..afe3708a4 --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionCeiling.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("ceiling")] + public class FunctionCeiling : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = Math.Ceiling(argument); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionChar.cs b/src/kOS.Safe/Function/KOSMath/FunctionChar.cs new file mode 100644 index 000000000..e3668be0d --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionChar.cs @@ -0,0 +1,16 @@ +using kOS.Safe.Encapsulation; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("char")] + public class FunctionChar : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + string result = new string((char)argument, 1); + ReturnValue = new StringValue(result); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionFloor.cs b/src/kOS.Safe/Function/KOSMath/FunctionFloor.cs new file mode 100644 index 000000000..19b1804cc --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionFloor.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("floor")] + public class FunctionFloor : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = Math.Floor(argument); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionLn.cs b/src/kOS.Safe/Function/KOSMath/FunctionLn.cs new file mode 100644 index 000000000..dc09e4dce --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionLn.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("ln")] + public class FunctionLn : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = Math.Log(argument); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionLog10.cs b/src/kOS.Safe/Function/KOSMath/FunctionLog10.cs new file mode 100644 index 000000000..0fea176eb --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionLog10.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("log10")] + public class FunctionLog10 : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = Math.Log10(argument); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionMax.cs b/src/kOS.Safe/Function/KOSMath/FunctionMax.cs new file mode 100644 index 000000000..ed2383c83 --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionMax.cs @@ -0,0 +1,20 @@ +using kOS.Safe.Compilation; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("max")] + public class FunctionMax : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + object argument1 = PopValueAssert(shared); + object argument2 = PopValueAssert(shared); + AssertArgBottomAndConsume(shared); + + var pair = new OperandPair(argument1, argument2); + Calculator calculator = Calculator.GetCalculator(pair); + object result = calculator.Max(pair); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionMin.cs b/src/kOS.Safe/Function/KOSMath/FunctionMin.cs new file mode 100644 index 000000000..c2228b1ba --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionMin.cs @@ -0,0 +1,20 @@ +using kOS.Safe.Compilation; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("min")] + public class FunctionMin : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + object argument1 = PopValueAssert(shared); + object argument2 = PopValueAssert(shared); + AssertArgBottomAndConsume(shared); + + var pair = new OperandPair(argument1, argument2); + Calculator calculator = Calculator.GetCalculator(pair); + object result = calculator.Min(pair); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionMod.cs b/src/kOS.Safe/Function/KOSMath/FunctionMod.cs new file mode 100644 index 000000000..ac31e9fde --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionMod.cs @@ -0,0 +1,15 @@ +namespace kOS.Safe.Function.KOSMath +{ + [Function("mod")] + public class FunctionMod : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double divisor = GetDouble(PopValueAssert(shared)); + double dividend = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = dividend % divisor; + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionRandom.cs b/src/kOS.Safe/Function/KOSMath/FunctionRandom.cs new file mode 100644 index 000000000..e45f42c66 --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionRandom.cs @@ -0,0 +1,17 @@ +using kOS.Safe.Encapsulation; +using System; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("random")] + public class FunctionRandom : SafeFunctionBase + { + private readonly Random random = new Random(); + + public override void Execute(SharedObjects shared) + { + AssertArgBottomAndConsume(shared); + ReturnValue = ScalarValue.Create(random.NextDouble()); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionRound.cs b/src/kOS.Safe/Function/KOSMath/FunctionRound.cs new file mode 100644 index 000000000..3cd3bde09 --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionRound.cs @@ -0,0 +1,34 @@ +using kOS.Safe.Exceptions; +using System; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("round")] + public class FunctionRound : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + int decimals; + int argCount = CountRemainingArgs(shared); + + switch (argCount) + { + case 1: + decimals = 0; + break; + + case 2: + decimals = GetInt(PopValueAssert(shared)); + break; + + default: + throw new KOSArgumentMismatchException(new[] { 1, 2 }, argCount); + } + + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = Math.Round(argument, decimals); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionSqrt.cs b/src/kOS.Safe/Function/KOSMath/FunctionSqrt.cs new file mode 100644 index 000000000..b0e7a9772 --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionSqrt.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("sqrt")] + public class FunctionSqrt : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = Math.Sqrt(argument); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/KOSMath/FunctionUnchar.cs b/src/kOS.Safe/Function/KOSMath/FunctionUnchar.cs new file mode 100644 index 000000000..b921337f5 --- /dev/null +++ b/src/kOS.Safe/Function/KOSMath/FunctionUnchar.cs @@ -0,0 +1,16 @@ +using kOS.Safe.Encapsulation; + +namespace kOS.Safe.Function.KOSMath +{ + [Function("unchar")] + public class FunctionUnchar : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + string argument = PopValueAssert(shared).ToString(); + AssertArgBottomAndConsume(shared); + char result = argument.ToCharArray()[0]; + ReturnValue = ScalarValue.Create(result); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/DebugDump.cs b/src/kOS.Safe/Function/Misc/DebugDump.cs new file mode 100644 index 000000000..332dd3a4c --- /dev/null +++ b/src/kOS.Safe/Function/Misc/DebugDump.cs @@ -0,0 +1,12 @@ +namespace kOS.Safe.Function.Misc +{ + [Function("debugdump")] + public class DebugDump : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + AssertArgBottomAndConsume(shared); + ReturnValue = shared.Cpu.DumpVariables(); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/FunctionLoad.cs b/src/kOS.Safe/Function/Misc/FunctionLoad.cs new file mode 100644 index 000000000..a095433de --- /dev/null +++ b/src/kOS.Safe/Function/Misc/FunctionLoad.cs @@ -0,0 +1,89 @@ +using kOS.Safe.Compilation; +using kOS.Safe.Encapsulation; +using kOS.Safe.Persistence; +using System.Collections.Generic; + +namespace kOS.Safe.Function.Misc +{ + [FunctionAttribute("load")] + public class FunctionLoad : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + bool defaultOutput = false; + bool justCompiling = false; // is this load() happening to compile, or to run? + string fileNameOut = null; + object topStack = PopValueAssert(shared, true); // null if there's no output file (output file means compile, not run). + if (topStack != null) + { + justCompiling = true; + string outputArg = topStack.ToString(); + if (outputArg.Equals("-default-compile-out-")) + defaultOutput = true; + else + fileNameOut = PersistenceUtilities.CookedFilename(outputArg, Volume.KOS_MACHINELANGUAGE_EXTENSION); + } + + string fileName = null; + topStack = PopValueAssert(shared, true); + if (topStack != null) + fileName = topStack.ToString(); + + AssertArgBottomAndConsume(shared); + + if (fileName == null) + throw new KOSFileException("No filename to load was given."); + + VolumeFile file = shared.VolumeMgr.CurrentVolume.Open(fileName, !justCompiling); // if running, look for KSM first. If compiling look for KS first. + if (file == null) throw new KOSFileException(string.Format("Can't find file '{0}'.", fileName)); + fileName = file.Name; // just in case GetByName picked an extension that changed it. + FileContent fileContent = file.ReadAll(); + + // filename is now guaranteed to have an extension. To make default output name, replace the extension with KSM: + if (defaultOutput) + fileNameOut = fileName.Substring(0, fileName.LastIndexOf('.')) + "." + Volume.KOS_MACHINELANGUAGE_EXTENSION; + + if (fileNameOut != null && fileName == fileNameOut) + throw new KOSFileException("Input and output filenames must differ."); + + if (shared.VolumeMgr == null) return; + if (shared.VolumeMgr.CurrentVolume == null) throw new KOSFileException("Volume not found"); + + if (shared.ScriptHandler != null) + { + shared.Cpu.StartCompileStopwatch(); + var options = new CompilerOptions { LoadProgramsInSameAddressSpace = true, FuncManager = shared.FunctionManager }; + string filePath = shared.VolumeMgr.GetVolumeRawIdentifier(shared.VolumeMgr.CurrentVolume) + "/" + fileName; + // add this program to the address space of the parent program, + // or to a file to save: + if (justCompiling) + { + List compileParts = shared.ScriptHandler.Compile(filePath, 1, fileContent.String, string.Empty, options); + VolumeFile volumeFile = shared.VolumeMgr.CurrentVolume.Save(fileNameOut, new FileContent(compileParts)); + if (volumeFile == null) + { + throw new KOSFileException("Can't save compiled file: not enough space or access forbidden"); + } + } + else + { + var programContext = shared.Cpu.SwitchToProgramContext(); + List parts; + if (fileContent.Category == FileCategory.KSM) + { + string prefix = programContext.Program.Count.ToString(); + parts = fileContent.AsParts(filePath, prefix); + } + else + { + parts = shared.ScriptHandler.Compile(filePath, 1, fileContent.String, "program", options); + } + int programAddress = programContext.AddObjectParts(parts); + // push the entry point address of the new program onto the stack + shared.Cpu.PushStack(programAddress); + } + shared.Cpu.StopCompileStopwatch(); + } + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/FunctionLogFile.cs b/src/kOS.Safe/Function/Misc/FunctionLogFile.cs new file mode 100644 index 000000000..79b0e6388 --- /dev/null +++ b/src/kOS.Safe/Function/Misc/FunctionLogFile.cs @@ -0,0 +1,34 @@ +using kOS.Safe.Encapsulation; +using kOS.Safe.Persistence; + +namespace kOS.Safe.Function.Misc +{ + [Function("logfile")] + public class FunctionLogFile : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + string fileName = PopValueAssert(shared, true).ToString(); + string expressionResult = PopValueAssert(shared).ToString(); + AssertArgBottomAndConsume(shared); + + if (shared.VolumeMgr != null) + { + Volume volume = shared.VolumeMgr.CurrentVolume; + if (volume != null) + { + VolumeFile volumeFile = volume.OpenOrCreate(fileName); + + if (volumeFile == null || !volumeFile.WriteLn(expressionResult)) + { + throw new KOSFileException("Can't append to file: not enough space or access forbidden"); + } + } + else + { + throw new KOSFileException("Volume not found"); + } + } + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/FunctionPrint.cs b/src/kOS.Safe/Function/Misc/FunctionPrint.cs new file mode 100644 index 000000000..0feb0a08c --- /dev/null +++ b/src/kOS.Safe/Function/Misc/FunctionPrint.cs @@ -0,0 +1,22 @@ +using kOS.Safe.Utilities; + +namespace kOS.Safe.Function.Misc +{ + [Function("print")] + public class FunctionPrint : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + string textToPrint = PopValueAssert(shared).ToString(); + AssertArgBottomAndConsume(shared); + if (shared.Screen == null) + { + SafeHouse.Logger.Log(textToPrint); + } + else + { + shared.Screen.Print(textToPrint); + } + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/FunctionReboot.cs b/src/kOS.Safe/Function/Misc/FunctionReboot.cs new file mode 100644 index 000000000..8471fb17f --- /dev/null +++ b/src/kOS.Safe/Function/Misc/FunctionReboot.cs @@ -0,0 +1,19 @@ +using kOS.Safe.Module; + +namespace kOS.Safe.Function.Misc +{ + [Function("reboot")] + public class FunctionReboot : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + if (shared.Processor != null) + { + AssertArgBottomAndConsume(shared); // not sure if this matters when rebooting anwyway. + shared.Processor.SetMode(ProcessorModes.OFF); + shared.Processor.SetMode(ProcessorModes.READY); + shared.Cpu.GetCurrentOpcode().AbortProgram = true; + } + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/FunctionRun.cs b/src/kOS.Safe/Function/Misc/FunctionRun.cs new file mode 100644 index 000000000..4e2ba0068 --- /dev/null +++ b/src/kOS.Safe/Function/Misc/FunctionRun.cs @@ -0,0 +1,88 @@ +using kOS.Safe.Compilation; +using kOS.Safe.Encapsulation; +using kOS.Safe.Execution; +using kOS.Safe.Persistence; +using System; +using System.Collections.Generic; + +namespace kOS.Safe.Function.Misc +{ + [Function("run")] + public class FunctionRun : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + // run() is strange. It needs two levels of args - the args to itself, and the args it is meant to + // pass on to the program it's invoking. First, these are the args to run itself: + object volumeId = PopValueAssert(shared, true); + string fileName = PopValueAssert(shared, true).ToString(); + AssertArgBottomAndConsume(shared); + + // Now the args it is going to be passing on to the program: + var progArgs = new List(); + int argc = CountRemainingArgs(shared); + for (int i = 0; i < argc; ++i) + progArgs.Add(PopValueAssert(shared, true)); + AssertArgBottomAndConsume(shared); + + if (shared.VolumeMgr == null) return; + if (shared.VolumeMgr.CurrentVolume == null) throw new Exception("Volume not found"); + + VolumeFile file = shared.VolumeMgr.CurrentVolume.Open(fileName, true); + if (file == null) throw new Exception(string.Format("File '{0}' not found", fileName)); + if (shared.ScriptHandler == null) return; + + if (volumeId != null) + { + throw new kOS.Safe.Exceptions.KOSException("Running programs on another processor is not supported"); + } + else + { + // clear the "program" compilation context + shared.Cpu.StartCompileStopwatch(); + shared.ScriptHandler.ClearContext("program"); + string filePath = shared.VolumeMgr.GetVolumeRawIdentifier(shared.VolumeMgr.CurrentVolume) + "/" + fileName; + var options = new CompilerOptions { LoadProgramsInSameAddressSpace = true, FuncManager = shared.FunctionManager }; + var programContext = shared.Cpu.SwitchToProgramContext(); + + List codeParts; + FileContent content = file.ReadAll(); + if (content.Category == FileCategory.KSM) + { + string prefix = programContext.Program.Count.ToString(); + codeParts = content.AsParts(fileName, prefix); + } + else + { + try + { + codeParts = shared.ScriptHandler.Compile(filePath, 1, content.String, "program", options); + } + catch (Exception) + { + // If it died due to a compile error, then we won't really be able to switch to program context + // as was implied by calling Cpu.SwitchToProgramContext() up above. The CPU needs to be + // told that it's still in interpreter context, or else it fails to advance the interpreter's + // instruction pointer and it will just try the "call run()" instruction again: + shared.Cpu.BreakExecution(false); + throw; + } + } + programContext.AddParts(codeParts); + shared.Cpu.StopCompileStopwatch(); + } + + // Because run() returns FIRST, and THEN the CPU jumps to the new program's first instruction that it set up, + // it needs to put the return stack in a weird order. Its return value needs to be buried UNDER the args to the + // program it's calling: + UsesAutoReturn = false; + + shared.Cpu.PushStack(0); // dummy return that all functions have. + + // Put the args for the program being called back on in the same order they were in before (so read the list backward): + shared.Cpu.PushStack(new KOSArgMarkerType()); + for (int i = argc - 1; i >= 0; --i) + shared.Cpu.PushStack(progArgs[i]); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/FunctionShutdown.cs b/src/kOS.Safe/Function/Misc/FunctionShutdown.cs new file mode 100644 index 000000000..393d0e945 --- /dev/null +++ b/src/kOS.Safe/Function/Misc/FunctionShutdown.cs @@ -0,0 +1,15 @@ +using kOS.Safe.Module; + +namespace kOS.Safe.Function.Misc +{ + [Function("shutdown")] + public class FunctionShutdown : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + AssertArgBottomAndConsume(shared); // not sure if this matters when shutting down anwyway. + if (shared.Processor != null) shared.Processor.SetMode(ProcessorModes.OFF); + shared.Cpu.GetCurrentOpcode().AbortProgram = true; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/MakeBuiltinDelegate.cs b/src/kOS.Safe/Function/Misc/MakeBuiltinDelegate.cs new file mode 100644 index 000000000..07bc7ae77 --- /dev/null +++ b/src/kOS.Safe/Function/Misc/MakeBuiltinDelegate.cs @@ -0,0 +1,16 @@ +using kOS.Safe.Encapsulation; + +namespace kOS.Safe.Function.Misc +{ + [Function("makebuiltindelegate")] + public class MakeBuiltinDelegate : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + string name = PopValueAssert(shared).ToString(); + AssertArgBottomAndConsume(shared); + + ReturnValue = new BuiltinDelegate(shared.Cpu, name); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Misc/ProfileResult.cs b/src/kOS.Safe/Function/Misc/ProfileResult.cs new file mode 100644 index 000000000..52790758d --- /dev/null +++ b/src/kOS.Safe/Function/Misc/ProfileResult.cs @@ -0,0 +1,26 @@ +using System.Text; + +namespace kOS.Safe.Function.Misc +{ + [Function("profileresult")] + public class ProfileResult : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + AssertArgBottomAndConsume(shared); + if (shared.Cpu.ProfileResult == null || shared.Cpu.ProfileResult.Count == 0) + { + ReturnValue = ""; + return; + } + StringBuilder sb = new StringBuilder(); + foreach (string textLine in shared.Cpu.ProfileResult) + { + if (sb.Length > 0) + sb.Append("\n"); + sb.Append(textLine); + } + ReturnValue = sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Persistence/FunctionCreate.cs b/src/kOS.Safe/Function/Persistence/FunctionCreate.cs new file mode 100644 index 000000000..5dde896c8 --- /dev/null +++ b/src/kOS.Safe/Function/Persistence/FunctionCreate.cs @@ -0,0 +1,18 @@ +using kOS.Safe.Encapsulation; + +namespace kOS.Safe.Function.Persistence +{ + [Function("create")] + public class FunctionCreate : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + string fileName = PopValueAssert(shared, true).ToString(); + AssertArgBottomAndConsume(shared); + + VolumeFile volumeFile = shared.VolumeMgr.CurrentVolume.Create(fileName); + + ReturnValue = volumeFile; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Persistence/FunctionDelete.cs b/src/kOS.Safe/Function/Persistence/FunctionDelete.cs new file mode 100644 index 000000000..b4d1502df --- /dev/null +++ b/src/kOS.Safe/Function/Persistence/FunctionDelete.cs @@ -0,0 +1,33 @@ +using kOS.Safe.Persistence; +using System; + +namespace kOS.Safe.Function.Persistence +{ + [Function("delete")] + public class FunctionDelete : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + object volumeId = PopValueAssert(shared, true); + string fileName = PopValueAssert(shared, true).ToString(); + AssertArgBottomAndConsume(shared); + + if (shared.VolumeMgr != null) + { + Volume volume = volumeId != null ? (volumeId is Volume ? volumeId as Volume : shared.VolumeMgr.GetVolume(volumeId)) : shared.VolumeMgr.CurrentVolume; + + if (volume != null) + { + if (!volume.Delete(fileName)) + { + throw new Exception(string.Format("File '{0}' not found", fileName)); + } + } + else + { + throw new Exception("Volume not found"); + } + } + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Persistence/FunctionExists.cs b/src/kOS.Safe/Function/Persistence/FunctionExists.cs new file mode 100644 index 000000000..d830fa712 --- /dev/null +++ b/src/kOS.Safe/Function/Persistence/FunctionExists.cs @@ -0,0 +1,14 @@ +namespace kOS.Safe.Function.Persistence +{ + [Function("exists")] + public class FunctionExists : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + string fileName = PopValueAssert(shared, true).ToString(); + AssertArgBottomAndConsume(shared); + + ReturnValue = shared.VolumeMgr.CurrentVolume.Exists(fileName); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Persistence/FunctionOpen.cs b/src/kOS.Safe/Function/Persistence/FunctionOpen.cs new file mode 100644 index 000000000..3b9830166 --- /dev/null +++ b/src/kOS.Safe/Function/Persistence/FunctionOpen.cs @@ -0,0 +1,24 @@ +using kOS.Safe.Encapsulation; +using kOS.Safe.Exceptions; + +namespace kOS.Safe.Function.Persistence +{ + [Function("open")] + public class FunctionOpen : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + string fileName = PopValueAssert(shared, true).ToString(); + AssertArgBottomAndConsume(shared); + + VolumeFile volumeFile = shared.VolumeMgr.CurrentVolume.Open(fileName); + + if (volumeFile == null) + { + throw new KOSException("File does not exist: " + fileName); + } + + ReturnValue = volumeFile; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Persistence/FunctionRename.cs b/src/kOS.Safe/Function/Persistence/FunctionRename.cs new file mode 100644 index 000000000..e27bc549a --- /dev/null +++ b/src/kOS.Safe/Function/Persistence/FunctionRename.cs @@ -0,0 +1,63 @@ +using kOS.Safe.Persistence; +using System; + +namespace kOS.Safe.Function.Persistence +{ + [Function("rename")] + public class FunctionRename : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + string newName = PopValueAssert(shared, true).ToString(); + // old file name or, when we're renaming a volume, the old volume name or Volume instance + object volumeIdOrOldName = PopValueAssert(shared, true); + string objectToRename = PopValueAssert(shared).ToString(); + AssertArgBottomAndConsume(shared); + + if (shared.VolumeMgr != null) + { + if (objectToRename == "file") + { + Volume volume = shared.VolumeMgr.CurrentVolume; + if (volume != null) + { + if (volume.Open(newName) == null) + { + if (!volume.RenameFile(volumeIdOrOldName.ToString(), newName)) + { + throw new Exception(string.Format("File '{0}' not found", volumeIdOrOldName)); + } + } + else + { + throw new Exception(string.Format("File '{0}' already exists.", newName)); + } + } + else + { + throw new Exception("Volume not found"); + } + } + else + { + Volume volume = volumeIdOrOldName is Volume ? volumeIdOrOldName as Volume : shared.VolumeMgr.GetVolume(volumeIdOrOldName); + if (volume != null) + { + if (volume.Renameable) + { + volume.Name = newName; + } + else + { + throw new Exception("Volume cannot be renamed"); + } + } + else + { + throw new Exception("Volume not found"); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Persistence/FunctionSwitch.cs b/src/kOS.Safe/Function/Persistence/FunctionSwitch.cs new file mode 100644 index 000000000..7a312db11 --- /dev/null +++ b/src/kOS.Safe/Function/Persistence/FunctionSwitch.cs @@ -0,0 +1,28 @@ +using kOS.Safe.Persistence; +using System; + +namespace kOS.Safe.Function.Persistence +{ + [Function("switch")] + public class FunctionSwitch : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + object volumeId = PopValueAssert(shared, true); + AssertArgBottomAndConsume(shared); + + if (shared.VolumeMgr != null) + { + Volume volume = volumeId is Volume ? volumeId as Volume : shared.VolumeMgr.GetVolume(volumeId); + if (volume != null) + { + shared.VolumeMgr.SwitchTo(volume); + } + else + { + throw new Exception("Volume not found"); + } + } + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/SafeFunctionBase.cs b/src/kOS.Safe/Function/SafeFunctionBase.cs new file mode 100644 index 000000000..aa90193b2 --- /dev/null +++ b/src/kOS.Safe/Function/SafeFunctionBase.cs @@ -0,0 +1,195 @@ +using kOS.Safe.Compilation; +using kOS.Safe.Encapsulation; +using kOS.Safe.Exceptions; +using System; +using System.Linq; + +namespace kOS.Safe.Function +{ + public abstract class SafeFunctionBase + { + /// + /// ALL FUNCTIONS in kOS will always have exactly one return value. We have no + /// "void" functions, to keep the execution logic consistent and simple. Therefore + /// even a function that never explicitly picks a return value still gets one by + /// default anyway that will be pushed on the stack after its Execute() is called. + /// That is what this property is for. If you wish your built-in function to put a + /// specific return value on the stack set its ReturnValue property in its Execute() + /// method, and the pushing of it onto the stack will be handled for you. Don't push + /// it onto the stack manually, as that would result in a double-push. + /// If you decline to set ReturnValue, it will get a default value of zero anyway. + /// + public object ReturnValue + { + get + { + // Convert from primitive types to encapsulated types so that functions + // do not explicitly need to return the encapsulated type. + return Structure.FromPrimitive(internalReturn); + } + set + { + internalReturn = value; + } + } + + private object internalReturn = 0; // really should be 'null', but kerboscript can't deal with that. + + /// + /// In the *extremely* rare case where a built-in function is NOT supposed to + /// push a value onto the stack as its return value, and these are very uncommon, + /// it should set this to false. By default it will be true. + /// Cases where this might occur are cases where the function will artificially mangle + /// the execution order by jumping the instruction pointer to somewhere else, in a way + /// other than with an actual proper jump or call opcode. Right now the function run() is the only + /// one that does this and needs the exception.
+ /// Think very carefully before setting this to false in your function, and only do so if + /// you really know what you're doing and have fully understood the system.
+ /// If you set this to false, it means you are manually manipulating the stack to give it your own + /// wierd return behavior. + ///
+ public bool UsesAutoReturn { get; set; } + + protected SafeFunctionBase() + { + ReturnValue = 0; // default return value ALL built-ins will have if they don't set it. + UsesAutoReturn = true; + } + + public abstract void Execute(SharedObjects shared); + + protected double GetDouble(object argument) + { + try + { + return Convert.ToDouble(argument); + } + catch (Exception) + { + throw new KOSCastException(argument.GetType(), typeof(ScalarValue)); + } + } + + protected int GetInt(object argument) + { + try + { + return Convert.ToInt32(argument); + } + catch (Exception) + { + throw new KOSCastException(argument.GetType(), typeof(ScalarValue)); + } + } + + protected double DegreesToRadians(double degrees) + { + return degrees * Math.PI / 180; + } + + protected double RadiansToDegrees(double radians) + { + return radians * 180 / Math.PI; + } + + /// + /// A utility function that a function's Execute() must use after it has popped all the + /// arguments it was expecting from the stack. It will assert that all the arguments + /// have been consumed exactly, and the next item on the stack is the arg bottom mark. + /// It will consume the arg bottom mark as well. + ///
+ /// If the assert fails, an exception is thrown. + ///
+ /// + protected void AssertArgBottomAndConsume(SharedObjects shared) + { + object shouldBeBottom = shared.Cpu.PopStack(); + if (shouldBeBottom != null && shouldBeBottom.GetType() == OpcodeCall.ArgMarkerType) + return; // Assert passed. + + throw new KOSArgumentMismatchException("Too many arguments were passed to " + GetFuncName()); + } + + /// + /// A utility function that a function's Execute() may use if it wishes to, to get a count of + /// how many args passed to it that it has not yet consumed still remain on the stack. + /// + /// + /// Number of args as yet unpopped. returns zero if there are no args, or -1 if there's a bug and the argstart marker is missing. + protected int CountRemainingArgs(SharedObjects shared) + { + int depth = 0; + bool found = false; + bool stillInStack = true; + while (stillInStack && !found) + { + object peekItem = shared.Cpu.PeekRaw(depth, out stillInStack); + if (stillInStack && peekItem != null && peekItem.GetType() == OpcodeCall.ArgMarkerType) + found = true; + else + ++depth; + } + if (found) + return depth; + else + return -1; + } + + /// + /// A utility function that a function's Execute() should use in place of cpu.PopValue(), + /// because it will assert that the value being popped is NOT an ARG_MARKER_STRING, and if it + /// is, it will throw the appropriate error. + /// + /// + protected object PopValueAssert(SharedObjects shared, bool barewordOkay = false) + { + object returnValue = shared.Cpu.PopValue(barewordOkay); + if (returnValue != null && returnValue.GetType() == OpcodeCall.ArgMarkerType) + throw new KOSArgumentMismatchException("Too few arguments were passed to " + GetFuncName()); + return returnValue; + } + + /// + /// A utility function that a function's Execute() should use in place of cpu.PopStack(), + /// because it will assert that the value being popped is NOT an ARG_MARKER_STRING, and if it + /// is, it will throw the appropriate error. + /// + /// + protected object PopStackAssert(SharedObjects shared) + { + object returnValue = shared.Cpu.PopStack(); + if (returnValue != null && returnValue.GetType() == OpcodeCall.ArgMarkerType) + throw new KOSArgumentMismatchException("Too few arguments were passed to " + GetFuncName()); + return returnValue; + } + + /// + /// Identical to PopValueAssert, but with the additional step of coercing the result + /// into a Structure to be sure, so it won't return primitives. + /// + /// value after coercion into a kOS Structure + protected Structure PopStructureAssertEncapsulated(SharedObjects shared, bool barewordOkay = false) + { + object returnValue = PopValueAssert(shared, barewordOkay); + return Structure.FromPrimitiveWithAssert(returnValue); + } + + protected string GetFuncName() + { + // The following is all just to extract the function name from the attribute. + // That really should be easier + string funcName = ""; // hopefully this cannot ever get seen by the user because of the next lines. + FunctionAttribute attr = (FunctionAttribute)GetType().GetCustomAttributes(typeof(FunctionAttribute), true).FirstOrDefault(); + if (attr != null) + { + // Of all the possible alias names, lets pick the longest one, as the most verbose description: + string longestOne = ""; + foreach (string name in attr.Names) + if (name.Length > longestOne.Length) + longestOne = name; + funcName = longestOne; + } + return funcName; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Suffixed/PIDLoopConstructor.cs b/src/kOS.Safe/Function/Suffixed/PIDLoopConstructor.cs new file mode 100644 index 000000000..1d8cfd80c --- /dev/null +++ b/src/kOS.Safe/Function/Suffixed/PIDLoopConstructor.cs @@ -0,0 +1,50 @@ +using kOS.Safe.Encapsulation; +using kOS.Safe.Exceptions; + +namespace kOS.Safe.Function.Suffixed +{ + [Function("pidloop")] + public class PIDLoopConstructor : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + int args = CountRemainingArgs(shared); + double kd; + double ki; + double kp; + double maxoutput; + double minoutput; + switch (args) + { + case 0: + this.ReturnValue = new PIDLoop(); + break; + + case 1: + kp = GetDouble(PopValueAssert(shared)); + this.ReturnValue = new PIDLoop(kp, 0, 0); + break; + + case 3: + kd = GetDouble(PopValueAssert(shared)); + ki = GetDouble(PopValueAssert(shared)); + kp = GetDouble(PopValueAssert(shared)); + this.ReturnValue = new PIDLoop(kp, ki, kd); + break; + + case 5: + maxoutput = GetDouble(PopValueAssert(shared)); + minoutput = GetDouble(PopValueAssert(shared)); + kd = GetDouble(PopValueAssert(shared)); + ki = GetDouble(PopValueAssert(shared)); + kp = GetDouble(PopValueAssert(shared)); + this.ReturnValue = new PIDLoop(kp, ki, kd, maxoutput, minoutput); + break; + + default: + throw new KOSArgumentMismatchException(new[] { 0, 1, 3, 5 }, args); + } + AssertArgBottomAndConsume(shared); + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Trigonometry/FunctionAngleDiff.cs b/src/kOS.Safe/Function/Trigonometry/FunctionAngleDiff.cs new file mode 100644 index 000000000..e04fbd3e3 --- /dev/null +++ b/src/kOS.Safe/Function/Trigonometry/FunctionAngleDiff.cs @@ -0,0 +1,15 @@ +namespace kOS.Safe.Function.Trigonometry +{ + [Function("anglediff")] + public class FunctionAngleDiff : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double ang2 = GetDouble(PopValueAssert(shared)); + double ang1 = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = kOS.Safe.Utilities.Math.DegreeFix(ang2 - ang1, -180); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Trigonometry/FunctionArcCos.cs b/src/kOS.Safe/Function/Trigonometry/FunctionArcCos.cs new file mode 100644 index 000000000..065f79d23 --- /dev/null +++ b/src/kOS.Safe/Function/Trigonometry/FunctionArcCos.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.Trigonometry +{ + [Function("arccos")] + public class FunctionArcCos : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = RadiansToDegrees(Math.Acos(argument)); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Trigonometry/FunctionArcSin.cs b/src/kOS.Safe/Function/Trigonometry/FunctionArcSin.cs new file mode 100644 index 000000000..a4f2164a6 --- /dev/null +++ b/src/kOS.Safe/Function/Trigonometry/FunctionArcSin.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.Trigonometry +{ + [Function("arcsin")] + public class FunctionArcSin : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = RadiansToDegrees(Math.Asin(argument)); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Trigonometry/FunctionArcTan.cs b/src/kOS.Safe/Function/Trigonometry/FunctionArcTan.cs new file mode 100644 index 000000000..a54e46b27 --- /dev/null +++ b/src/kOS.Safe/Function/Trigonometry/FunctionArcTan.cs @@ -0,0 +1,16 @@ +using System; + +namespace kOS.Safe.Function.Trigonometry +{ + [Function("arctan")] + public class FunctionArcTan : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double argument = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = RadiansToDegrees(Math.Atan(argument)); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Trigonometry/FunctionArcTan2.cs b/src/kOS.Safe/Function/Trigonometry/FunctionArcTan2.cs new file mode 100644 index 000000000..9ca16f0ac --- /dev/null +++ b/src/kOS.Safe/Function/Trigonometry/FunctionArcTan2.cs @@ -0,0 +1,17 @@ +using System; + +namespace kOS.Safe.Function.Trigonometry +{ + [Function("arctan2")] + public class FunctionArcTan2 : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double x = GetDouble(PopValueAssert(shared)); + double y = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double result = RadiansToDegrees(Math.Atan2(y, x)); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Trigonometry/FunctionCos.cs b/src/kOS.Safe/Function/Trigonometry/FunctionCos.cs new file mode 100644 index 000000000..1cd966b45 --- /dev/null +++ b/src/kOS.Safe/Function/Trigonometry/FunctionCos.cs @@ -0,0 +1,17 @@ +using System; + +namespace kOS.Safe.Function.Trigonometry +{ + [Function("cos")] + public class FunctionCos : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double degrees = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double radians = DegreesToRadians(degrees); + double result = Math.Cos(radians); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Trigonometry/FunctionSin.cs b/src/kOS.Safe/Function/Trigonometry/FunctionSin.cs new file mode 100644 index 000000000..cd5d811fe --- /dev/null +++ b/src/kOS.Safe/Function/Trigonometry/FunctionSin.cs @@ -0,0 +1,17 @@ +using System; + +namespace kOS.Safe.Function.Trigonometry +{ + [Function("sin")] + public class FunctionSin : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double degrees = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double radians = DegreesToRadians(degrees); + double result = Math.Sin(radians); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Function/Trigonometry/FunctionTan.cs b/src/kOS.Safe/Function/Trigonometry/FunctionTan.cs new file mode 100644 index 000000000..500f92328 --- /dev/null +++ b/src/kOS.Safe/Function/Trigonometry/FunctionTan.cs @@ -0,0 +1,17 @@ +using System; + +namespace kOS.Safe.Function.Trigonometry +{ + [Function("tan")] + public class FunctionTan : SafeFunctionBase + { + public override void Execute(SharedObjects shared) + { + double degrees = GetDouble(PopValueAssert(shared)); + AssertArgBottomAndConsume(shared); + double radians = DegreesToRadians(degrees); + double result = Math.Tan(radians); + ReturnValue = result; + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Module/IProcessor.cs b/src/kOS.Safe/Module/IProcessor.cs index c9430d5e1..4ac81dcfb 100644 --- a/src/kOS.Safe/Module/IProcessor.cs +++ b/src/kOS.Safe/Module/IProcessor.cs @@ -3,9 +3,12 @@ namespace kOS.Safe.Module public interface IProcessor { void SetMode(ProcessorModes newProcessorMode); + //ProcessorModes GetMode(); string BootFilename { get; set; } bool CheckCanBoot(); + + void FixedUpdate(); } public enum ProcessorModes { READY, STARVED, OFF }; } \ No newline at end of file diff --git a/src/kOS.Safe/Utilities/AssemblyWalkAttribute.cs b/src/kOS.Safe/Utilities/AssemblyWalkAttribute.cs new file mode 100644 index 000000000..75d38b455 --- /dev/null +++ b/src/kOS.Safe/Utilities/AssemblyWalkAttribute.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace kOS.Safe.Utilities +{ + [System.AttributeUsage((System.AttributeTargets.Class | System.AttributeTargets.Struct), Inherited = false, AllowMultiple = false)] + public class AssemblyWalkAttribute : Attribute + { + public Type AttributeType { get; set; } + public Type InterfaceType { get; set; } + public Type InherritedType { get; set; } + public string StaticWalkMethod { get; set; } + public string StaticRegisterMethod { get; set; } + public int LoadedCount { get; set; } + + public static readonly Dictionary AllWalkAttributes = new Dictionary(); + public static readonly Dictionary AttributesToWalk = new Dictionary(); + + public AssemblyWalkAttribute() + { + LoadedCount = 0; + } + + public override string ToString() + { + if (AttributeType != null) + return AttributeType.FullName; + if (InterfaceType != null) + return InterfaceType.FullName; + if (InherritedType != null) + return InherritedType.FullName; + return "StaticWalk"; + } + + public static void Walk() + { + LoadWalkAttributes(); + WalkAllAssemblies(); + } + + public static void LoadWalkAttributes() + { + AllWalkAttributes.Clear(); + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in assemblies) + { + if (!assembly.GlobalAssemblyCache) + { + SafeHouse.Logger.LogWarning("Loading assembly: " + assembly.FullName); + try + { + LoadWalkAttribute(assembly); + } + catch + { + SafeHouse.Logger.LogWarning("Error while loading assembly: " + assembly.FullName + ", skipping assembly."); + } + } + } + } + + public static void LoadWalkAttribute(Assembly assembly) + { + foreach (Type type in assembly.GetTypes()) + { + var attr = type.GetCustomAttributes(typeof(AssemblyWalkAttribute), true).FirstOrDefault() as AssemblyWalkAttribute; + if (attr != null) + { + AllWalkAttributes.Add(attr, type); + } + } + } + + public static void WalkAllAssemblies() + { + SafeHouse.Logger.LogWarning("Begin walking assemblies."); + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in assemblies) + { + if (!assembly.GlobalAssemblyCache) + { + SafeHouse.Logger.LogWarning("Walking assembly: " + assembly.FullName); + try + { + WalkAssembly(assembly); + } + catch + { + SafeHouse.Logger.LogWarning("Error while loading assembly: " + assembly.FullName + ", skipping assembly."); + } + } + } + SafeHouse.Logger.LogWarning("Begin static walk methods."); + foreach (AssemblyWalkAttribute walkAttribute in AllWalkAttributes.Keys) + { + if (!string.IsNullOrEmpty(walkAttribute.StaticWalkMethod)) + { + Type managerType = AllWalkAttributes[walkAttribute]; + var walkMethod = managerType.GetMethod(walkAttribute.StaticWalkMethod, BindingFlags.Static | BindingFlags.Public); + walkMethod.Invoke(null, null); + } + SafeHouse.Logger.LogWarning("Attribute " + walkAttribute.ToString() + " loaded " + walkAttribute.LoadedCount + " objects."); + } + SafeHouse.Logger.LogWarning("Finish walking assemblies."); + } + + public static void WalkAssembly(Assembly assembly) + { + foreach (Type type in assembly.GetTypes()) + { + foreach (AssemblyWalkAttribute walkAttribute in AllWalkAttributes.Keys) + { + if (!string.IsNullOrEmpty(walkAttribute.StaticRegisterMethod)) + { + Type managerType = AllWalkAttributes[walkAttribute]; + var managerRegisterMethod = managerType.GetMethod(walkAttribute.StaticRegisterMethod, BindingFlags.Static | BindingFlags.Public); + if (managerRegisterMethod != null) + { + if (walkAttribute.AttributeType != null) + { + var attr = type.GetCustomAttributes(walkAttribute.AttributeType, false).FirstOrDefault(); + if (attr != null) + { + managerRegisterMethod.Invoke(null, new[] { attr, type }); + walkAttribute.LoadedCount++; + } + } + else if (walkAttribute.InterfaceType != null) + { + bool isInterface = !type.IsInterface && type.GetInterfaces().Contains(walkAttribute.InterfaceType); + if (isInterface) + { + managerRegisterMethod.Invoke(null, new[] { type }); + walkAttribute.LoadedCount++; + } + } + else if (walkAttribute.InherritedType != null) + { + if (!type.IsAbstract && walkAttribute.InherritedType.IsAssignableFrom(type)) + { + managerRegisterMethod.Invoke(null, new[] { type }); + walkAttribute.LoadedCount++; + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/kOS.Safe/Utilities/Math.cs b/src/kOS.Safe/Utilities/Math.cs index 35be773ee..c64c003c4 100644 --- a/src/kOS.Safe/Utilities/Math.cs +++ b/src/kOS.Safe/Utilities/Math.cs @@ -91,5 +91,26 @@ public static float ClampToIndent(float val, float min, float max, float increme return outVal; } + + /// + /// Fix the strange too-large or too-small angle degrees that are sometimes + /// returned by KSP, normalizing them into a constrained 360 degree range. + /// + /// input angle in degrees + /// + /// Bottom of 360 degree range to normalize to. + /// ( 0 means the range [0..360]), while -180 means [-180,180] ) + /// + /// the same angle, normalized to the range given. + public static double DegreeFix(double inAngle, double rangeStart) + { + double rangeEnd = rangeStart + 360.0; + double outAngle = inAngle; + while (outAngle > rangeEnd) + outAngle -= 360.0; + while (outAngle < rangeStart) + outAngle += 360.0; + return outAngle; + } } } diff --git a/src/kOS.Safe/kOS.Safe.csproj b/src/kOS.Safe/kOS.Safe.csproj index 4427244d7..19b419105 100644 --- a/src/kOS.Safe/kOS.Safe.csproj +++ b/src/kOS.Safe/kOS.Safe.csproj @@ -50,6 +50,7 @@ + @@ -173,8 +174,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -206,6 +251,7 @@ + diff --git a/src/kOS.sln b/src/kOS.sln index 19ce9d9d9..e20bbc89f 100644 --- a/src/kOS.sln +++ b/src/kOS.sln @@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kOS.Safe", "kOS.Safe\kOS.Sa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kOS.Safe.Test", "kOS.Safe.Test\kOS.Safe.Test.csproj", "{C9A42A44-DDC8-4D6C-8A16-D7F30F494B46}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kOS.CommandLine", "kOS.CommandLine\kOS.CommandLine.csproj", "{8AB664CD-E0AD-4D8F-B654-1D084E840703}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -28,6 +30,13 @@ Global {C9A42A44-DDC8-4D6C-8A16-D7F30F494B46}.Debug|Any CPU.Build.0 = Debug|Any CPU {C9A42A44-DDC8-4D6C-8A16-D7F30F494B46}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9A42A44-DDC8-4D6C-8A16-D7F30F494B46}.Release|Any CPU.Build.0 = Release|Any CPU + {8AB664CD-E0AD-4D8F-B654-1D084E840703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AB664CD-E0AD-4D8F-B654-1D084E840703}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AB664CD-E0AD-4D8F-B654-1D084E840703}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AB664CD-E0AD-4D8F-B654-1D084E840703}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 @@ -42,7 +51,4 @@ Global $2.inheritsScope = text/x-csharp $2.scope = text/x-csharp EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection EndGlobal diff --git a/src/kOS/Binding/Binding.cs b/src/kOS/Binding/Binding.cs index dc8214961..48a07c537 100644 --- a/src/kOS/Binding/Binding.cs +++ b/src/kOS/Binding/Binding.cs @@ -1,8 +1,12 @@ namespace kOS.Binding { - public abstract class Binding + public abstract class Binding : kOS.Safe.Binding.SafeBinding { - public virtual void AddTo(SharedObjects shared) { } + public override void AddTo(Safe.SharedObjects shared) + { + AddTo(shared as SharedObjects); + } + public abstract void AddTo(SharedObjects shared); public virtual void Update() { } } } diff --git a/src/kOS/Binding/BindingManager.cs b/src/kOS/Binding/BindingManager.cs index e7769316e..cddea3c23 100644 --- a/src/kOS/Binding/BindingManager.cs +++ b/src/kOS/Binding/BindingManager.cs @@ -3,14 +3,17 @@ using System.Linq; using System.Reflection; using kOS.Safe.Binding; +using kOS.Safe.Utilities; namespace kOS.Binding { + [AssemblyWalk(AttributeType = typeof(BindingAttribute), StaticRegisterMethod = "RegisterMethod")] public class BindingManager : IDisposable, IBindingManager { private readonly SharedObjects shared; - private readonly List bindings = new List(); + private readonly List bindings = new List(); private readonly Dictionary variables; + private static readonly Dictionary rawAttributes = new Dictionary(); private FlightControlManager flightControl; @@ -30,13 +33,11 @@ public void Load() variables.Clear(); flightControl = null; - foreach (var t in Assembly.GetExecutingAssembly().GetTypes()) + foreach (BindingAttribute attr in rawAttributes.Keys) { - var attr = (BindingAttribute)t.GetCustomAttributes(typeof(BindingAttribute), true).FirstOrDefault(); - if (attr == null) continue; + var t = rawAttributes[attr]; if (attr.Contexts.Any() && !attr.Contexts.Intersect(contexts).Any()) continue; - - var b = (Binding)Activator.CreateInstance(t); + var b = (kOS.Safe.Binding.SafeBinding)Activator.CreateInstance(t); b.AddTo(shared); bindings.Add(b); @@ -48,6 +49,14 @@ public void Load() } } + public static void RegisterMethod(BindingAttribute attr, Type type) + { + if (attr != null && !rawAttributes.ContainsKey(attr)) + { + rawAttributes.Add(attr, type); + } + } + public void AddBoundVariable(string name, BindingGetDlg getDelegate, BindingSetDlg setDelegate) { BoundVariable variable; diff --git a/src/kOS/Binding/FlightControlManager.cs b/src/kOS/Binding/FlightControlManager.cs index b0d07d730..04a5a047a 100644 --- a/src/kOS/Binding/FlightControlManager.cs +++ b/src/kOS/Binding/FlightControlManager.cs @@ -13,7 +13,7 @@ namespace kOS.Binding { [Binding("ksp")] - public class FlightControlManager : Binding , IDisposable + public class FlightControlManager : Binding, IDisposable { private Vessel currentVessel; private readonly Dictionary flightParameters = new Dictionary(); diff --git a/src/kOS/Function/FunctionBase.cs b/src/kOS/Function/FunctionBase.cs index c9bd25839..e2fec6a85 100644 --- a/src/kOS/Function/FunctionBase.cs +++ b/src/kOS/Function/FunctionBase.cs @@ -1,87 +1,18 @@ -using System; -using System.Linq; -using kOS.Suffixed; -using kOS.Safe.Exceptions; -using kOS.Safe.Compilation; +using kOS.Safe.Exceptions; using kOS.Safe.Function; +using kOS.Suffixed; +using System; using TimeSpan = kOS.Suffixed.TimeSpan; -using kOS.Safe.Encapsulation; namespace kOS.Function { - public abstract class FunctionBase + public abstract class FunctionBase : SafeFunctionBase { - /// - /// ALL FUNCTIONS in kOS will always have exactly one return value. We have no - /// "void" functions, to keep the execution logic consistent and simple. Therefore - /// even a function that never explicitly picks a return value still gets one by - /// default anyway that will be pushed on the stack after its Execute() is called. - /// That is what this property is for. If you wish your built-in function to put a - /// specific return value on the stack set its ReturnValue property in its Execute() - /// method, and the pushing of it onto the stack will be handled for you. Don't push - /// it onto the stack manually, as that would result in a double-push. - /// If you decline to set ReturnValue, it will get a default value of zero anyway. - /// - public object ReturnValue - { - get - { - // Convert from primitive types to encapsulated types so that functions - // do not explicitly need to return the encapsulated type. - return Structure.FromPrimitive(internalReturn); - } - set - { - internalReturn = value; - } - } - private object internalReturn = 0; // really should be 'null', but kerboscript can't deal with that. - - /// - /// In the *extremely* rare case where a built-in function is NOT supposed to - /// push a value onto the stack as its return value, and these are very uncommon, - /// it should set this to false. By default it will be true. - /// Cases where this might occur are cases where the function will artificially mangle - /// the execution order by jumping the instruction pointer to somewhere else, in a way - /// other than with an actual proper jump or call opcode. Right now the function run() is the only - /// one that does this and needs the exception.
- /// Think very carefully before setting this to false in your function, and only do so if - /// you really know what you're doing and have fully understood the system.
- /// If you set this to false, it means you are manually manipulating the stack to give it your own - /// wierd return behavior. - ///
- public bool UsesAutoReturn {get; set;} - - protected FunctionBase() - { - ReturnValue = 0; // default return value ALL built-ins will have if they don't set it. - UsesAutoReturn = true; - } - public abstract void Execute(SharedObjects shared); - protected double GetDouble(object argument) + public override void Execute(Safe.SharedObjects shared) { - try - { - return Convert.ToDouble(argument); - } - catch(Exception) - { - throw new KOSCastException(argument.GetType(),typeof(ScalarValue)); - } - } - - protected int GetInt(object argument) - { - try - { - return Convert.ToInt32(argument); - } - catch (Exception) - { - throw new KOSCastException(argument.GetType(),typeof(ScalarValue)); - } + Execute(shared as SharedObjects); } protected Vector GetVector(object argument) @@ -91,7 +22,7 @@ protected Vector GetVector(object argument) { return vector; } - throw new KOSCastException(argument.GetType(),typeof(Vector)); + throw new KOSCastException(argument.GetType(), typeof(Vector)); } protected RgbaColor GetRgba(object argument) @@ -101,7 +32,7 @@ protected RgbaColor GetRgba(object argument) { return rgba; } - throw new KOSCastException(argument.GetType(),typeof(RgbaColor)); + throw new KOSCastException(argument.GetType(), typeof(RgbaColor)); } protected TimeSpan GetTimeSpan(object argument) @@ -130,117 +61,7 @@ protected Orbitable GetOrbitable(object argument) { return orbitable; } - throw new KOSCastException(argument.GetType(),typeof(Orbitable)); - } - - protected double DegreesToRadians(double degrees) - { - return degrees * Math.PI / 180; - } - - protected double RadiansToDegrees(double radians) - { - return radians * 180 / Math.PI; - } - - /// - /// A utility function that a function's Execute() must use after it has popped all the - /// arguments it was expecting from the stack. It will assert that all the arguments - /// have been consumed exactly, and the next item on the stack is the arg bottom mark. - /// It will consume the arg bottom mark as well. - ///
- /// If the assert fails, an exception is thrown. - ///
- /// - protected void AssertArgBottomAndConsume(SharedObjects shared) - { - object shouldBeBottom = shared.Cpu.PopStack(); - if (shouldBeBottom != null && shouldBeBottom.GetType() == OpcodeCall.ArgMarkerType) - return; // Assert passed. - - throw new KOSArgumentMismatchException("Too many arguments were passed to " + GetFuncName()); - } - - /// - /// A utility function that a function's Execute() may use if it wishes to, to get a count of - /// how many args passed to it that it has not yet consumed still remain on the stack. - /// - /// - /// Number of args as yet unpopped. returns zero if there are no args, or -1 if there's a bug and the argstart marker is missing. - protected int CountRemainingArgs(SharedObjects shared) - { - int depth = 0; - bool found = false; - bool stillInStack = true; - while (stillInStack && !found) - { - object peekItem = shared.Cpu.PeekRaw(depth, out stillInStack); - if (stillInStack && peekItem != null && peekItem.GetType() == OpcodeCall.ArgMarkerType) - found = true; - else - ++depth; - } - if (found) - return depth; - else - return -1; - } - - /// - /// A utility function that a function's Execute() should use in place of cpu.PopValue(), - /// because it will assert that the value being popped is NOT an ARG_MARKER_STRING, and if it - /// is, it will throw the appropriate error. - /// - /// - protected object PopValueAssert(SharedObjects shared, bool barewordOkay = false) - { - object returnValue = shared.Cpu.PopValue(barewordOkay); - if (returnValue != null && returnValue.GetType() == OpcodeCall.ArgMarkerType) - throw new KOSArgumentMismatchException("Too few arguments were passed to " + GetFuncName()); - return returnValue; - } - - /// - /// A utility function that a function's Execute() should use in place of cpu.PopStack(), - /// because it will assert that the value being popped is NOT an ARG_MARKER_STRING, and if it - /// is, it will throw the appropriate error. - /// - /// - protected object PopStackAssert(SharedObjects shared) - { - object returnValue = shared.Cpu.PopStack(); - if (returnValue != null && returnValue.GetType() == OpcodeCall.ArgMarkerType) - throw new KOSArgumentMismatchException("Too few arguments were passed to " + GetFuncName()); - return returnValue; - } - - /// - /// Identical to PopValueAssert, but with the additional step of coercing the result - /// into a Structure to be sure, so it won't return primitives. - /// - /// value after coercion into a kOS Structure - protected Structure PopStructureAssertEncapsulated(SharedObjects shared, bool barewordOkay = false) - { - object returnValue = PopValueAssert(shared, barewordOkay); - return Structure.FromPrimitiveWithAssert(returnValue); - } - - protected string GetFuncName() - { - // The following is all just to extract the function name from the attribute. - // That really should be easier - string funcName = ""; // hopefully this cannot ever get seen by the user because of the next lines. - FunctionAttribute attr = (FunctionAttribute)GetType().GetCustomAttributes(typeof(FunctionAttribute), true).FirstOrDefault(); - if (attr != null) - { - // Of all the possible alias names, lets pick the longest one, as the most verbose description: - string longestOne = ""; - foreach (string name in attr.Names) - if (name.Length > longestOne.Length) - longestOne = name; - funcName = longestOne; - } - return funcName; + throw new KOSCastException(argument.GetType(), typeof(Orbitable)); } } -} +} \ No newline at end of file diff --git a/src/kOS/Function/FunctionManager.cs b/src/kOS/Function/FunctionManager.cs deleted file mode 100644 index 2aa3f819e..000000000 --- a/src/kOS/Function/FunctionManager.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using kOS.Safe.Function; - -namespace kOS.Function -{ - public class FunctionManager : IFunctionManager - { - private readonly SharedObjects shared; - private Dictionary functions; - - public FunctionManager(SharedObjects shared) - { - this.shared = shared; - } - - public void Load() - { - functions = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) - { - var attr = (FunctionAttribute)type.GetCustomAttributes(typeof(FunctionAttribute), true).FirstOrDefault(); - if (attr == null) continue; - - object functionObject = Activator.CreateInstance(type); - foreach (string functionName in attr.Names) - { - if (functionName != string.Empty) - { - functions.Add(functionName, (FunctionBase)functionObject); - } - } - } - } - - public void CallFunction(string functionName) - { - if (!functions.ContainsKey(functionName)) - { - throw new Exception("Call to non-existent function " + functionName); - } - - FunctionBase function = functions[functionName]; - function.Execute(shared); - if (function.UsesAutoReturn) - shared.Cpu.PushStack(function.ReturnValue); - } - - /// - /// Find out if the function with the name given exists already in the built-in hardcoded functions set - /// (as opposed to the user functions). - /// - /// check if this function exists - /// true if it does exist (as a built-in, not as a user function) - public bool Exists(string functionName) - { - return functions.ContainsKey(functionName); - } - - } -} diff --git a/src/kOS/Function/Math.cs b/src/kOS/Function/Math.cs index 7484d0fa8..71cb45fba 100644 --- a/src/kOS/Function/Math.cs +++ b/src/kOS/Function/Math.cs @@ -1,169 +1,9 @@ -using System; -using kOS.Safe.Compilation; +using kOS.Safe.Exceptions; using kOS.Safe.Function; using kOS.Suffixed; -using kOS.Safe.Exceptions; -using kOS.Safe.Encapsulation; namespace kOS.Function { - [Function("abs")] - public class FunctionAbs : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = Math.Abs(argument); - ReturnValue = result; - } - } - - [Function("mod")] - public class FunctionMod : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double divisor = GetDouble(PopValueAssert(shared)); - double dividend = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = dividend % divisor; - ReturnValue = result; - } - } - - [Function("floor")] - public class FunctionFloor : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = Math.Floor(argument); - ReturnValue = result; - } - } - - [Function("ceiling")] - public class FunctionCeiling : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = Math.Ceiling(argument); - ReturnValue = result; - } - } - - [Function("round")] - public class FunctionRound : FunctionBase - { - public override void Execute(SharedObjects shared) - { - int decimals; - int argCount = CountRemainingArgs(shared); - - switch (argCount) - { - case 1: - decimals = 0; - break; - case 2: - decimals = GetInt(PopValueAssert(shared)); - break; - default: - throw new KOSArgumentMismatchException(new []{1,2}, argCount); - } - - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = Math.Round(argument, decimals); - ReturnValue = result; - } - } - - [Function("sqrt")] - public class FunctionSqrt : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = Math.Sqrt(argument); - ReturnValue = result; - } - } - - - [Function("ln")] - public class FunctionLn : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = Math.Log(argument); - ReturnValue = result; - } - } - - [Function("log10")] - public class FunctionLog10 : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = Math.Log10(argument); - ReturnValue = result; - } - } - - [Function("min")] - public class FunctionMin : FunctionBase - { - public override void Execute(SharedObjects shared) - { - object argument1 = PopValueAssert(shared); - object argument2 = PopValueAssert(shared); - AssertArgBottomAndConsume(shared); - - var pair = new OperandPair(argument1, argument2); - Calculator calculator = Calculator.GetCalculator(pair); - object result = calculator.Min(pair); - ReturnValue = result; - } - } - - [Function("max")] - public class FunctionMax : FunctionBase - { - public override void Execute(SharedObjects shared) - { - object argument1 = PopValueAssert(shared); - object argument2 = PopValueAssert(shared); - AssertArgBottomAndConsume(shared); - - var pair = new OperandPair(argument1, argument2); - Calculator calculator = Calculator.GetCalculator(pair); - object result = calculator.Max(pair); - ReturnValue = result; - } - } - - [Function("random")] - public class FunctionRandom : FunctionBase - { - private readonly Random random = new Random(); - - public override void Execute(SharedObjects shared) - { - AssertArgBottomAndConsume(shared); - ReturnValue = Structure.FromPrimitive(random.NextDouble()); - } - } - [Function("vcrs", "vectorcrossproduct")] public class FunctionVectorCross : FunctionBase { @@ -239,29 +79,4 @@ public override void Execute(SharedObjects shared) throw new KOSException("vector angle calculation attempted with a non-vector value"); } } - - - [Function("char")] - public class FunctionChar : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - string result = new string((char) argument, 1); - ReturnValue = new StringValue(result); - } - } - - [Function("unchar")] - public class FunctionUnchar : FunctionBase - { - public override void Execute(SharedObjects shared) - { - string argument = PopValueAssert(shared).ToString(); - AssertArgBottomAndConsume(shared); - char result = argument.ToCharArray()[0]; - ReturnValue = ScalarValue.Create(result); - } - } -} +} \ No newline at end of file diff --git a/src/kOS/Function/Misc.cs b/src/kOS/Function/Misc.cs index 5708780b1..9ad234762 100644 --- a/src/kOS/Function/Misc.cs +++ b/src/kOS/Function/Misc.cs @@ -1,19 +1,14 @@ -using kOS.Execution; -using kOS.Safe.Compilation; +using kOS.Module; +using kOS.Safe.Compilation.KS; +using kOS.Safe.Encapsulation; using kOS.Safe.Exceptions; using kOS.Safe.Execution; using kOS.Safe.Function; -using kOS.Safe.Module; using kOS.Safe.Persistence; using kOS.Safe.Utilities; using kOS.Suffixed; -using System; -using System.Text; -using System.Collections.Generic; using kOS.Suffixed.PartModuleField; -using kOS.Module; -using kOS.Safe.Compilation.KS; -using kOS.Safe.Encapsulation; +using System; namespace kOS.Function { @@ -27,17 +22,6 @@ public override void Execute(SharedObjects shared) } } - [Function("print")] - public class FunctionPrint : FunctionBase - { - public override void Execute(SharedObjects shared) - { - string textToPrint = PopValueAssert(shared).ToString(); - AssertArgBottomAndConsume(shared); - shared.Screen.Print(textToPrint); - } - } - [Function("hudtext")] public class FunctionHudText : FunctionBase { @@ -137,184 +121,6 @@ public override void Execute(SharedObjects shared) } } - [Function("run")] - public class FunctionRun : FunctionBase - { - public override void Execute(SharedObjects shared) - { - // run() is strange. It needs two levels of args - the args to itself, and the args it is meant to - // pass on to the program it's invoking. First, these are the args to run itself: - object volumeId = PopValueAssert(shared, true); - string fileName = PopValueAssert(shared, true).ToString(); - AssertArgBottomAndConsume(shared); - - // Now the args it is going to be passing on to the program: - var progArgs = new List(); - int argc = CountRemainingArgs(shared); - for (int i = 0; i < argc; ++i) - progArgs.Add(PopValueAssert(shared, true)); - AssertArgBottomAndConsume(shared); - - if (shared.VolumeMgr == null) return; - if (shared.VolumeMgr.CurrentVolume == null) throw new Exception("Volume not found"); - - VolumeFile file = shared.VolumeMgr.CurrentVolume.Open(fileName, true); - if (file == null) throw new Exception(string.Format("File '{0}' not found", fileName)); - if (shared.ScriptHandler == null) return; - - if (volumeId != null) - { - Volume targetVolume = shared.VolumeMgr.GetVolume(volumeId); - if (targetVolume != null) - { - if (shared.ProcessorMgr != null) - { - string filePath = string.Format("{0}/{1}", shared.VolumeMgr.GetVolumeRawIdentifier(targetVolume), fileName); - var options = new CompilerOptions { LoadProgramsInSameAddressSpace = true, FuncManager = shared.FunctionManager }; - List parts = shared.ScriptHandler.Compile(filePath, 1, file.ReadAll().String, "program", options); - var builder = new ProgramBuilder(); - builder.AddRange(parts); - List program = builder.BuildProgram(); - shared.ProcessorMgr.RunProgramOn(program, targetVolume); - } - } - else - { - throw new KOSFileException("Volume not found"); - } - } - else - { - // clear the "program" compilation context - shared.Cpu.StartCompileStopwatch(); - shared.ScriptHandler.ClearContext("program"); - string filePath = shared.VolumeMgr.GetVolumeRawIdentifier(shared.VolumeMgr.CurrentVolume) + "/" + fileName; - var options = new CompilerOptions { LoadProgramsInSameAddressSpace = true, FuncManager = shared.FunctionManager }; - var programContext = ((CPU)shared.Cpu).SwitchToProgramContext(); - - List codeParts; - FileContent content = file.ReadAll(); - if (content.Category == FileCategory.KSM) - { - string prefix = programContext.Program.Count.ToString(); - codeParts = content.AsParts(fileName, prefix); - } - else - { - try - { - codeParts = shared.ScriptHandler.Compile(filePath, 1, content.String, "program", options); - } - catch (Exception) - { - // If it died due to a compile error, then we won't really be able to switch to program context - // as was implied by calling Cpu.SwitchToProgramContext() up above. The CPU needs to be - // told that it's still in interpreter context, or else it fails to advance the interpreter's - // instruction pointer and it will just try the "call run()" instruction again: - shared.Cpu.BreakExecution(false); - throw; - } - } - programContext.AddParts(codeParts); - shared.Cpu.StopCompileStopwatch(); - } - - // Because run() returns FIRST, and THEN the CPU jumps to the new program's first instruction that it set up, - // it needs to put the return stack in a weird order. Its return value needs to be buried UNDER the args to the - // program it's calling: - UsesAutoReturn = false; - - shared.Cpu.PushStack(0); // dummy return that all functions have. - - // Put the args for the program being called back on in the same order they were in before (so read the list backward): - shared.Cpu.PushStack(new KOSArgMarkerType()); - for (int i = argc - 1; i >= 0; --i) - shared.Cpu.PushStack(progArgs[i]); - } - } - - [FunctionAttribute("load")] - public class FunctionLoad : FunctionBase - { - public override void Execute(SharedObjects shared) - { - bool defaultOutput = false; - bool justCompiling = false; // is this load() happening to compile, or to run? - string fileNameOut = null; - object topStack = PopValueAssert(shared, true); // null if there's no output file (output file means compile, not run). - if (topStack != null) - { - justCompiling = true; - string outputArg = topStack.ToString(); - if (outputArg.Equals("-default-compile-out-")) - defaultOutput = true; - else - fileNameOut = PersistenceUtilities.CookedFilename(outputArg, Volume.KOS_MACHINELANGUAGE_EXTENSION); - } - - string fileName = null; - topStack = PopValueAssert(shared, true); - if (topStack != null) - fileName = topStack.ToString(); - - AssertArgBottomAndConsume(shared); - - if (fileName == null) - throw new KOSFileException("No filename to load was given."); - - VolumeFile file = shared.VolumeMgr.CurrentVolume.Open(fileName, !justCompiling); // if running, look for KSM first. If compiling look for KS first. - if (file == null) throw new KOSFileException(string.Format("Can't find file '{0}'.", fileName)); - fileName = file.Name; // just in case GetByName picked an extension that changed it. - FileContent fileContent = file.ReadAll(); - - // filename is now guaranteed to have an extension. To make default output name, replace the extension with KSM: - if (defaultOutput) - fileNameOut = fileName.Substring(0, fileName.LastIndexOf('.')) + "." + Volume.KOS_MACHINELANGUAGE_EXTENSION; - - if (fileNameOut != null && fileName == fileNameOut) - throw new KOSFileException("Input and output filenames must differ."); - - if (shared.VolumeMgr == null) return; - if (shared.VolumeMgr.CurrentVolume == null) throw new KOSFileException("Volume not found"); - - if (shared.ScriptHandler != null) - { - shared.Cpu.StartCompileStopwatch(); - var options = new CompilerOptions { LoadProgramsInSameAddressSpace = true, FuncManager = shared.FunctionManager }; - string filePath = shared.VolumeMgr.GetVolumeRawIdentifier(shared.VolumeMgr.CurrentVolume) + "/" + fileName; - // add this program to the address space of the parent program, - // or to a file to save: - if (justCompiling) - { - List compileParts = shared.ScriptHandler.Compile(filePath, 1, fileContent.String, string.Empty, options); - VolumeFile volumeFile = shared.VolumeMgr.CurrentVolume.Save(fileNameOut, new FileContent(compileParts)); - if (volumeFile == null) - { - throw new KOSFileException("Can't save compiled file: not enough space or access forbidden"); - } - } - else - { - var programContext = ((CPU)shared.Cpu).SwitchToProgramContext(); - List parts; - if (fileContent.Category == FileCategory.KSM) - { - string prefix = programContext.Program.Count.ToString(); - parts = fileContent.AsParts(filePath, prefix); - } - else - { - parts = shared.ScriptHandler.Compile(filePath, 1, fileContent.String, "program", options); - } - int programAddress = programContext.AddObjectParts(parts); - // push the entry point address of the new program onto the stack - shared.Cpu.PushStack(programAddress); - } - shared.Cpu.StopCompileStopwatch(); - } - } - } - [Function("add")] public class FunctionAddNode : FunctionBase { @@ -337,93 +143,6 @@ public override void Execute(SharedObjects shared) } } - [Function("logfile")] - public class FunctionLogFile : FunctionBase - { - public override void Execute(SharedObjects shared) - { - string fileName = PopValueAssert(shared, true).ToString(); - string expressionResult = PopValueAssert(shared).ToString(); - AssertArgBottomAndConsume(shared); - - if (shared.VolumeMgr != null) - { - Volume volume = shared.VolumeMgr.CurrentVolume; - if (volume != null) - { - VolumeFile volumeFile = volume.OpenOrCreate(fileName); - - if (volumeFile == null || !volumeFile.WriteLn(expressionResult)) - { - throw new KOSFileException("Can't append to file: not enough space or access forbidden"); - } - } - else - { - throw new KOSFileException("Volume not found"); - } - } - } - } - - [Function("reboot")] - public class FunctionReboot : FunctionBase - { - public override void Execute(SharedObjects shared) - { - if (shared.Processor != null) - { - AssertArgBottomAndConsume(shared); // not sure if this matters when rebooting anwyway. - shared.Processor.SetMode(ProcessorModes.OFF); - shared.Processor.SetMode(ProcessorModes.READY); - ((CPU)shared.Cpu).GetCurrentOpcode().AbortProgram = true; - } - } - } - - [Function("shutdown")] - public class FunctionShutdown : FunctionBase - { - public override void Execute(SharedObjects shared) - { - AssertArgBottomAndConsume(shared); // not sure if this matters when shutting down anwyway. - if (shared.Processor != null) shared.Processor.SetMode(ProcessorModes.OFF); - ((CPU)shared.Cpu).GetCurrentOpcode().AbortProgram = true; - } - } - - [Function("debugdump")] - public class DebugDump : FunctionBase - { - public override void Execute(SharedObjects shared) - { - AssertArgBottomAndConsume(shared); - ReturnValue = shared.Cpu.DumpVariables(); - } - } - - [Function("profileresult")] - public class ProfileResult : FunctionBase - { - public override void Execute(SharedObjects shared) - { - AssertArgBottomAndConsume(shared); - if (shared.Cpu.ProfileResult == null || shared.Cpu.ProfileResult.Count == 0) - { - ReturnValue = ""; - return; - } - StringBuilder sb = new StringBuilder(); - foreach (string textLine in shared.Cpu.ProfileResult) - { - if (sb.Length > 0 ) - sb.Append("\n"); - sb.Append(textLine); - } - ReturnValue = sb.ToString(); - } - } - [Function("warpto")] public class WarpTo : FunctionBase { @@ -445,7 +164,7 @@ public override void Execute(SharedObjects shared) TimeWarp.fetch.WarpTo(ut); } } - + [Function("processor")] public class FunctionProcessor : FunctionBase { @@ -456,11 +175,16 @@ public override void Execute(SharedObjects shared) kOSProcessor processor; - if (processorTagOrVolume is Volume) { + if (processorTagOrVolume is Volume) + { processor = shared.ProcessorMgr.GetProcessor(processorTagOrVolume as Volume); - } else if (processorTagOrVolume is string || processorTagOrVolume is StringValue) { + } + else if (processorTagOrVolume is string || processorTagOrVolume is StringValue) + { processor = shared.ProcessorMgr.GetProcessor(processorTagOrVolume.ToString()); - } else { + } + else + { throw new KOSInvalidArgumentException("processor", "processorId", "String or Volume expected"); } @@ -472,57 +196,4 @@ public override void Execute(SharedObjects shared) ReturnValue = PartModuleFieldsFactory.Construct(processor, shared); } } - - [Function("pidloop")] - public class PIDLoopConstructor : FunctionBase - { - public override void Execute(SharedObjects shared) - { - int args = CountRemainingArgs(shared); - double kd; - double ki; - double kp; - double maxoutput; - double minoutput; - switch (args) - { - case 0: - this.ReturnValue = new PIDLoop(); - break; - case 1: - kp = GetDouble(PopValueAssert(shared)); - this.ReturnValue = new PIDLoop(kp, 0, 0); - break; - case 3: - kd = GetDouble(PopValueAssert(shared)); - ki = GetDouble(PopValueAssert(shared)); - kp = GetDouble(PopValueAssert(shared)); - this.ReturnValue = new PIDLoop(kp, ki, kd); - break; - case 5: - maxoutput = GetDouble(PopValueAssert(shared)); - minoutput = GetDouble(PopValueAssert(shared)); - kd = GetDouble(PopValueAssert(shared)); - ki = GetDouble(PopValueAssert(shared)); - kp = GetDouble(PopValueAssert(shared)); - this.ReturnValue = new PIDLoop(kp, ki, kd, maxoutput, minoutput); - break; - default: - throw new KOSArgumentMismatchException(new[] { 0, 1, 3, 5 }, args); - } - AssertArgBottomAndConsume(shared); - } - } - - [Function("makebuiltindelegate")] - public class MakeBuiltinDelegate : FunctionBase - { - public override void Execute(SharedObjects shared) - { - string name = PopValueAssert(shared).ToString(); - AssertArgBottomAndConsume(shared); - - ReturnValue = new BuiltinDelegate(shared.Cpu, name); - } - } -} +} \ No newline at end of file diff --git a/src/kOS/Function/Persistence.cs b/src/kOS/Function/Persistence.cs index 75cab0d85..4843450a8 100644 --- a/src/kOS/Function/Persistence.cs +++ b/src/kOS/Function/Persistence.cs @@ -9,29 +9,6 @@ namespace kOS.Function { - [Function("switch")] - public class FunctionSwitch : FunctionBase - { - public override void Execute(SharedObjects shared) - { - object volumeId = PopValueAssert(shared, true); - AssertArgBottomAndConsume(shared); - - if (shared.VolumeMgr != null) - { - Volume volume = volumeId is Volume ? volumeId as Volume : shared.VolumeMgr.GetVolume(volumeId); - if (volume != null) - { - shared.VolumeMgr.SwitchTo(volume); - } - else - { - throw new Exception("Volume not found"); - } - } - } - } - [Function("edit")] public class FunctionEdit : FunctionBase { @@ -105,92 +82,6 @@ public override void Execute(SharedObjects shared) } } - [Function("rename")] - public class FunctionRename : FunctionBase - { - public override void Execute(SharedObjects shared) - { - string newName = PopValueAssert(shared, true).ToString(); - // old file name or, when we're renaming a volume, the old volume name or Volume instance - object volumeIdOrOldName = PopValueAssert(shared, true); - string objectToRename = PopValueAssert(shared).ToString(); - AssertArgBottomAndConsume(shared); - - if (shared.VolumeMgr != null) - { - if (objectToRename == "file") - { - Volume volume = shared.VolumeMgr.CurrentVolume; - if (volume != null) - { - if (volume.Open(newName) == null) - { - if (!volume.RenameFile(volumeIdOrOldName.ToString(), newName)) - { - throw new Exception(string.Format("File '{0}' not found", volumeIdOrOldName)); - } - } - else - { - throw new Exception(string.Format("File '{0}' already exists.", newName)); - } - } - else - { - throw new Exception("Volume not found"); - } - } - else - { - Volume volume = volumeIdOrOldName is Volume ? volumeIdOrOldName as Volume : shared.VolumeMgr.GetVolume(volumeIdOrOldName); - if (volume != null) - { - if (volume.Renameable) - { - volume.Name = newName; - } - else - { - throw new Exception("Volume cannot be renamed"); - } - } - else - { - throw new Exception("Volume not found"); - } - } - } - } - } - - [Function("delete")] - public class FunctionDelete : FunctionBase - { - public override void Execute(SharedObjects shared) - { - object volumeId = PopValueAssert(shared, true); - string fileName = PopValueAssert(shared, true).ToString(); - AssertArgBottomAndConsume(shared); - - if (shared.VolumeMgr != null) - { - Volume volume = volumeId != null ? (volumeId is Volume ? volumeId as Volume : shared.VolumeMgr.GetVolume(volumeId)) : shared.VolumeMgr.CurrentVolume; - - if (volume != null) - { - if (!volume.Delete(fileName)) - { - throw new Exception(string.Format("File '{0}' not found", fileName)); - } - } - else - { - throw new Exception("Volume not found"); - } - } - } - } - [Function("writejson")] public class FunctionWriteJson : FunctionBase { @@ -236,49 +127,4 @@ public override void Execute(SharedObjects shared) ReturnValue = read; } } - - [Function("exists")] - public class FunctionExists : FunctionBase - { - public override void Execute(SharedObjects shared) - { - string fileName = PopValueAssert(shared, true).ToString(); - AssertArgBottomAndConsume(shared); - - ReturnValue = shared.VolumeMgr.CurrentVolume.Exists(fileName); - } - } - - [Function("open")] - public class FunctionOpen : FunctionBase - { - public override void Execute(SharedObjects shared) - { - string fileName = PopValueAssert(shared, true).ToString(); - AssertArgBottomAndConsume(shared); - - VolumeFile volumeFile = shared.VolumeMgr.CurrentVolume.Open(fileName); - - if (volumeFile == null) - { - throw new KOSException("File does not exist: " + fileName); - } - - ReturnValue = volumeFile; - } - } - - [Function("create")] - public class FunctionCreate : FunctionBase - { - public override void Execute(SharedObjects shared) - { - string fileName = PopValueAssert(shared, true).ToString(); - AssertArgBottomAndConsume(shared); - - VolumeFile volumeFile = shared.VolumeMgr.CurrentVolume.Create(fileName); - - ReturnValue = volumeFile; - } - } } \ No newline at end of file diff --git a/src/kOS/Function/Suffixed.cs b/src/kOS/Function/Suffixed.cs index d6d93acc5..5edb4d0a5 100644 --- a/src/kOS/Function/Suffixed.cs +++ b/src/kOS/Function/Suffixed.cs @@ -184,98 +184,6 @@ public override void Execute(SharedObjects shared) } } - [Function("list")] - public class FunctionList : FunctionBase - { - public override void Execute(SharedObjects shared) - { - Structure[] argArray = new Structure[CountRemainingArgs(shared)]; - for (int i = argArray.Length - 1 ; i >= 0 ; --i) - argArray[i] = PopStructureAssertEncapsulated(shared); // fill array in reverse order because .. stack args. - AssertArgBottomAndConsume(shared); - var listValue = new ListValue(argArray.ToList()); - ReturnValue = listValue; - } - } - - [Function("queue")] - public class FunctionQueue : FunctionBase - { - public override void Execute(SharedObjects shared) - { - Structure[] argArray = new Structure[CountRemainingArgs(shared)]; - for (int i = argArray.Length - 1 ; i >= 0 ; --i) - argArray[i] = PopStructureAssertEncapsulated(shared); // fill array in reverse order because .. stack args. - AssertArgBottomAndConsume(shared); - var queueValue = new QueueValue(argArray.ToList()); - ReturnValue = queueValue; - } - } - - [Function("stack")] - public class FunctionStack : FunctionBase - { - public override void Execute(SharedObjects shared) - { - Structure[] argArray = new Structure[CountRemainingArgs(shared)]; - for (int i = argArray.Length - 1 ; i >= 0 ; --i) - argArray[i] = PopStructureAssertEncapsulated(shared); // fill array in reverse order because .. stack args. - AssertArgBottomAndConsume(shared); - var stackValue = new StackValue(argArray.ToList()); - ReturnValue = stackValue; - } - } - - [Function("range")] - public class FunctionRange : FunctionBase - { - public override void Execute(SharedObjects shared) - { - // Default values for parameters - int from = RangeValue.DEFAULT_START; - int to = RangeValue.DEFAULT_STOP; - int step = RangeValue.DEFAULT_STEP; - - int argCount = CountRemainingArgs(shared); - // assign parameter values from the stack, pop them in reverse order - switch (argCount) - { - case 1: - to = GetInt(PopStructureAssertEncapsulated(shared)); - break; - case 2: - to = GetInt(PopStructureAssertEncapsulated(shared)); - from = GetInt(PopStructureAssertEncapsulated(shared)); - break; - case 3: - step = GetInt(PopStructureAssertEncapsulated(shared)); - to = GetInt(PopStructureAssertEncapsulated(shared)); - from = GetInt(PopStructureAssertEncapsulated(shared)); - break; - default: - throw new KOSArgumentMismatchException(new int[] { 1, 2, 3 }, argCount, "Thrown from function RANGE()"); - } - AssertArgBottomAndConsume(shared); - - ReturnValue = new RangeValue(from, to, step); - } - } - - [Function("lex", "lexicon")] - public class FunctionLexicon : FunctionBase - { - public override void Execute(SharedObjects shared) - { - - Structure[] argArray = new Structure[CountRemainingArgs(shared)]; - for (int i = argArray.Length - 1 ; i >= 0 ; --i) - argArray[i] = PopStructureAssertEncapsulated(shared); // fill array in reverse order because .. stack args. - AssertArgBottomAndConsume(shared); - var lexicon = new Lexicon(argArray.ToList()); - ReturnValue = lexicon; - } - } - [Function("hsv")] public class FunctionHsv : FunctionBase { diff --git a/src/kOS/Function/Trigonometry.cs b/src/kOS/Function/Trigonometry.cs deleted file mode 100644 index bcb3e00b9..000000000 --- a/src/kOS/Function/Trigonometry.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using kOS.Safe.Function; -using kOS.Safe.Encapsulation; - -namespace kOS.Function -{ - [Function("sin")] - public class FunctionSin : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double degrees = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double radians = DegreesToRadians(degrees); - double result = Math.Sin(radians); - ReturnValue = result; - } - } - - [Function("cos")] - public class FunctionCos : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double degrees = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double radians = DegreesToRadians(degrees); - double result = Math.Cos(radians); - ReturnValue = result; - } - } - - [Function("tan")] - public class FunctionTan : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double degrees = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double radians = DegreesToRadians(degrees); - double result = Math.Tan(radians); - ReturnValue = result; - } - } - - [Function("arcsin")] - public class FunctionArcSin : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = RadiansToDegrees(Math.Asin(argument)); - ReturnValue = result; - } - } - - [Function("arccos")] - public class FunctionArcCos : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = RadiansToDegrees(Math.Acos(argument)); - ReturnValue = result; - } - } - - [Function("arctan")] - public class FunctionArcTan : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double argument = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = RadiansToDegrees(Math.Atan(argument)); - ReturnValue = result; - } - } - - [Function("arctan2")] - public class FunctionArcTan2 : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double x = GetDouble(PopValueAssert(shared)); - double y = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = RadiansToDegrees(Math.Atan2(y, x)); - ReturnValue = result; - } - } - - [Function("anglediff")] - public class FunctionAngleDiff : FunctionBase - { - public override void Execute(SharedObjects shared) - { - double ang2 = GetDouble(PopValueAssert(shared)); - double ang1 = GetDouble(PopValueAssert(shared)); - AssertArgBottomAndConsume(shared); - double result = kOS.Utilities.Utils.DegreeFix( ang2 - ang1, -180 ); - ReturnValue = result; - } - } -} diff --git a/src/kOS/Module/Bootstrapper.cs b/src/kOS/Module/Bootstrapper.cs index b6c1da0b0..a209c2849 100644 --- a/src/kOS/Module/Bootstrapper.cs +++ b/src/kOS/Module/Bootstrapper.cs @@ -25,6 +25,8 @@ public void Start() CheckForLegacyArchive(); KOSNomenclature.PopulateMapping(typeof(kOS.Safe.Encapsulation.Structure).Assembly, this.GetType().Assembly); + + AssemblyWalkAttribute.Walk(); } private void BuildEnvironment() diff --git a/src/kOS/Module/kOSProcessor.cs b/src/kOS/Module/kOSProcessor.cs index 675e25250..b20df2cc6 100644 --- a/src/kOS/Module/kOSProcessor.cs +++ b/src/kOS/Module/kOSProcessor.cs @@ -21,6 +21,7 @@ using kOS.Safe.Execution; using UnityEngine; using kOS.Safe.Encapsulation; +using kOS.Safe.Function; namespace kOS.Module { diff --git a/src/kOS/Screen/Interpreter.cs b/src/kOS/Screen/Interpreter.cs index fe59b0ad0..1c7e9477b 100644 --- a/src/kOS/Screen/Interpreter.cs +++ b/src/kOS/Screen/Interpreter.cs @@ -142,7 +142,7 @@ protected void CompileCommand(string commandText) List commandParts = Shared.ScriptHandler.Compile("interpreter history", commandHistoryIndex, commandText, "interpreter", options); if (commandParts == null) return; - var interpreterContext = ((CPU)Shared.Cpu).GetInterpreterContext(); + var interpreterContext = Shared.Cpu.GetInterpreterContext(); interpreterContext.AddParts(commandParts); } catch (Exception e) diff --git a/src/kOS/Utilities/Utils.cs b/src/kOS/Utilities/Utils.cs index 780f0c249..e56ce337e 100644 --- a/src/kOS/Utilities/Utils.cs +++ b/src/kOS/Utilities/Utils.cs @@ -93,13 +93,7 @@ private static IEnumerable GetActualAttachedNodes(Part checkPart) /// the same angle, normalized to the range given. public static double DegreeFix(double inAngle, double rangeStart) { - double rangeEnd = rangeStart + 360.0; - double outAngle = inAngle; - while (outAngle > rangeEnd) - outAngle -= 360.0; - while (outAngle < rangeStart) - outAngle += 360.0; - return outAngle; + return kOS.Safe.Utilities.Math.DegreeFix(inAngle, rangeStart); } public static GUISkin GetSkinCopy(GUISkin toCopy) diff --git a/src/kOS/kOS.csproj b/src/kOS/kOS.csproj index 041cd8574..68224141e 100644 --- a/src/kOS/kOS.csproj +++ b/src/kOS/kOS.csproj @@ -79,13 +79,11 @@ - - @@ -224,7 +222,5 @@ --> - - - - + + \ No newline at end of file