public double EvaluateFunctionCall(ref int position, uint functionNameHash, int nbParams, double[] param, Stack <int> returnStack, Stack <double> valueStack) { var found = Functions.TryGetValue(functionNameHash, out var fun); if (!found) { throw new Exception("Tried to call an undefined function '" + DbgStr(functionNameHash) + "' at position " + position + "\r\nKnown functions: " + string.Join(", ", Functions.Keys.Select(DbgStr)) + "\r\nAs a string: " + TryDeref(functionNameHash) + "?" + "\r\nKnown symbols: " + string.Join(", ", DebugSymbols.Keys.Select(DbgStr))); } if (fun.Kind != FuncDef.Custom) { return(EvaluateBuiltInFunction(ref position, fun.Kind, nbParams, param, returnStack, valueStack)); } // handle functions that are defined in the program _memory.Variables.PushScope(param); // write parameters into new scope returnStack.Push(position); // set position for 'cret' call position = Functions.Get(functionNameHash).StartPosition; // move pointer to start of function return(NanTags.VoidReturn()); // return no value, continue execution elsewhere }
/// <summary> /// Set a value by name. If no scope has it, then it will be defined in the innermost scope /// </summary> public void SetValue(uint crushedName, double newValue) { unchecked { for (int i = _currentScopeIdx; i >= 0; i--) { var current = _scopes[i]; if (!current.Get(crushedName, out var oldValue)) { continue; } // ReSharper disable once CompareOfFloatsByEqualityOperator if (oldValue == newValue) { return; } if (NanTags.IsAllocated(oldValue)) { PotentialGarbage.Add(newValue); } current.Put(crushedName, newValue, true); return; } // nothing already exists to replace _scopes[_currentScopeIdx].Add(crushedName, newValue); } }
private void DoIndexedGet(Stack <double> valueStack, ushort paramCount) { var target = valueStack.Pop(); var value = _memory.Variables.Resolve(NanTags.DecodeVariableRef(target)); var type = NanTags.TypeOf(value); switch (type) { // Note: Numeric types should read the bit at index // Hashes and arrays do the obvious lookup // Sets return true/false for occupancy case DataType.PtrString: case DataType.PtrStaticString: // get the other indexes. If more than one, build a string out of the bits? // What to do with out-of-range? var str = _memory.DereferenceString(NanTags.DecodePointer(value)); var sb = new StringBuilder(paramCount - 1); for (int i = 0; i < paramCount; i++) { var idx = _memory.CastInt(valueStack.Pop()); if (idx >= 0 && idx < str.Length) { sb.Append(str[idx]); } } var result = _memory.StoreStringAndGetReference(sb.ToString()); valueStack.Push(result); return; default: throw new Exception("Indexing of type '" + type + "' is not yet supported"); } }
public double CastDouble(double encoded) { var type = NanTags.TypeOf(encoded); double result; switch (type) { case DataType.Number: return(encoded); case DataType.ValInt32: return(NanTags.DecodeInt32(encoded)); case DataType.ValUInt32: return(NanTags.DecodeUInt32(encoded)); case DataType.VariableRef: // Follow scope var next = Variables.Resolve(NanTags.DecodeVariableRef(encoded)); return(CastDouble(next)); case DataType.ValSmallString: double.TryParse(NanTags.DecodeShortStr(encoded), out result); return(result); case DataType.PtrStaticString: case DataType.PtrString: NanTags.DecodePointer(encoded, out var target, out _); double.TryParse(DereferenceString(target), out result); return(result); // All the things that can't be meaningfully cast default: return(0.0d); } }
private void HandleMemoryAccess(char action, Stack <double> valueStack, int position, uint varRef, ushort paramCount) { switch (action) { case 'g': // get (adds a value to the stack, false if not set) valueStack.Push(_memory.Variables.Resolve(varRef)); break; case 's': // set if (valueStack.Count < 1) { throw new Exception("There were no values to save. Did you forget a `return` in a function? Position: " + position); } _memory.Variables.SetValue(varRef, valueStack.Pop()); break; case 'i': // is set? (adds a bool to the stack) valueStack.Push(NanTags.EncodeBool(_memory.Variables.CanResolve(varRef))); break; case 'u': // unset _memory.Variables.Remove(varRef); break; case 'G': // indexed get DoIndexedGet(valueStack, paramCount); break; default: throw new Exception("Unknown memory opcode: '" + action + "'"); } }
/// <summary> /// Map parameter names to positional names. This must match the expectations of the interpreter /// </summary> private static void ParameterPositions(Scope parameterNames, IEnumerable <string> paramNames, NanCodeWriter wr) { parameterNames.PushScope(); var i = 0; foreach (var paramName in paramNames) { if (parameterNames.InScope(NanTags.GetCrushedName(paramName))) { throw new Exception("Duplicate parameter '" + paramName + "'.\r\n" + "All parameter names must be unique in a single function definition."); } var originalReference = NanTags.GetCrushedName(paramName); var parameterReference = Scope.NameFor(i); var parameterByteCode = NanTags.EncodeVariableRef(parameterReference); parameterNames.SetValue(originalReference, parameterByteCode); wr.AddSymbol(originalReference, paramName); wr.AddSymbol(parameterReference, "param[" + i + "]"); i++; } }
static Scope() { posParamHash = new uint[128]; // this limits the number of parameters, so is quite high for (int i = 0; i < 128; i++) { posParamHash[i] = NanTags.GetCrushedName("__p" + i); } }
private string Stringify(double token, DataType type, HashLookup <string> debugSymbols) { switch (type) { case DataType.Invalid: return(""); case DataType.NoValue: return(""); case DataType.VariableRef: var rref = NanTags.DecodeVariableRef(token); if (debugSymbols?.ContainsKey(rref) == true) { return("'" + debugSymbols[rref] + "' (" + rref.ToString("X") + ")"); } return(rref.ToString("X")); case DataType.Opcode: NanTags.DecodeLongOpCode(token, out var ccls, out var cact, out var refr); if (ccls == 'm') { if (debugSymbols?.ContainsKey(refr) == true) { return(ccls + "" + cact + " '" + debugSymbols[refr] + "' (" + refr.ToString("X") + ")"); } } if (ccls == 'i') { if (debugSymbols?.ContainsKey(refr) == true) { return("Incr " + ((sbyte)cact) + " '" + debugSymbols[refr] + "' (" + refr.ToString("X") + ")"); } } NanTags.DecodeOpCode(token, out _, out _, out var p1, out var p2); return(ccls + "" + cact + " (" + p1 + ", " + p2 + ")"); case DataType.ValSmallString: return("[" + NanTags.DecodeShortStr(token) + "]"); case DataType.PtrHashtable: case DataType.PtrGrid: case DataType.PtrSet: case DataType.PtrVector: case DataType.PtrString: case DataType.PtrStaticString: NanTags.DecodePointer(token, out var targ, out _); return(" -> " + targ); case DataType.ValInt32: return(NanTags.DecodeInt32(token).ToString()); case DataType.ValUInt32: return(NanTags.DecodeUInt32(token).ToString()); case DataType.Number: return(token.ToString(CultureInfo.InvariantCulture)); default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } }
public static HashLookup <string> DebugSymbolsForBuiltIns() { var tmp = new HashLookup <string>(64); Action <string> add = name => { tmp.Add(NanTags.GetCrushedName(name), name); }; add("="); add("equals"); add(">"); add("<"); add("<>"); add("not-equal"); add("assert"); add("random"); add("eval"); add("call"); add("not"); add("or"); add("and"); add("readkey"); add("readline"); add("print"); add("substring"); add("length"); add("replace"); add("concat"); add("+"); add("-"); add("*"); add("/"); add("%"); add("()"); return(tmp); }
public void encoding_and_decoding_unsigned_int32() { unchecked { for (int i = 1; i > 0; i += i) { uint original = (uint)i * 3; double enc = NanTags.EncodeUInt32(original); uint result = NanTags.DecodeUInt32(enc); Assert.That(result, Is.EqualTo(original)); } } }
public void int32_overflows_the_same_way_as_native() { unchecked { for (int i = 1; i > 0; i += i) { int original = i * 2; double enc = NanTags.EncodeInt32(original); int result = NanTags.DecodeInt32(enc); Assert.That(result, Is.EqualTo(original)); } } }
public void doubles_are_interpreted_as_numeric() { var rnd = new Random(); for (int i = 0; i < 10000; i++) { var d = rnd.NextDouble() / i; var inv = 1.0d / d + double.Epsilon; Assert.That(NanTags.TypeOf(d), Is.EqualTo(DataType.Number)); Assert.That(NanTags.TypeOf(inv), Is.EqualTo(DataType.Number)); } }
/// <summary> /// Store a new string at the end of memory, and return a string pointer token for it /// </summary> public double StoreStringAndGetReference(string str) { // short strings are stack/scope values if (str.Length <= 6) { return(NanTags.EncodeShortStr(str)); } // Longer strings need to be allocated var location = encodedTokens.Count; var bytes = Encoding.ASCII.GetBytes(str); var headerOpCode = NanTags.EncodeUInt32((uint)bytes.Length); encodedTokens.Add(headerOpCode); unchecked { ulong pack = 0; int rem = 0; for (int i = 0; i < bytes.Length; i++) { pack += ((ulong)bytes[i]) << rem; rem += 8; if (rem > 56) { encodedTokens.Add(BitConverter.Int64BitsToDouble((long)pack)); rem = 0; pack = 0; } } if (rem != 0) { for (; rem < 64; rem += 8) { pack += ((ulong)'_') << rem; } encodedTokens.Add(BitConverter.Int64BitsToDouble((long)pack)); } } var token = NanTags.EncodePointer(location, DataType.PtrString); Variables.PotentialGarbage.Add(token); return(token); }
private int HandleFunctionDefinition(ushort argCount, ushort tokenCount, int position, Stack <double> valueStack) { var functionNameHash = NanTags.DecodeVariableRef(valueStack.Pop()); if (Functions.ContainsKey(functionNameHash)) { throw new Exception("Function '" + functionNameHash + "' redefined at " + position + ". Original at " + Functions[functionNameHash]); } Functions.Add(functionNameHash, new FunctionDefinition { StartPosition = position, ParamCount = argCount }); return(position + tokenCount + 1); // + definition length + terminator }
/// <summary> /// Remove innermost scope, and drop back to the previous one /// </summary> public void DropScope() { var last = _scopes[_currentScopeIdx]; _scopes[_currentScopeIdx] = null; _currentScopeIdx--; // this could be done on another thread foreach (var token in last.Values) { if (NanTags.IsAllocated(token)) { PotentialGarbage.Add(token); } } }
/// <summary> /// Produce a diagnostic description of the memory layout /// </summary> public string ToString(HashLookup <string> debugSymbols) { int index = 0; var sb = new StringBuilder(); // Try to display static strings meaningfully if (NanTags.TypeOf(encodedTokens[0]) == DataType.Opcode) { index = 1; NanTags.DecodeLongOpCode(encodedTokens[0], out var c1, out var c2, out var count); if (c1 == 'c' && c2 == 's') { sb.AppendLine("Data table: " + count + " tokens (" + (count * 8) + " bytes)"); while (index < count) { var length = NanTags.DecodeUInt32(encodedTokens[index]); var chunkCount = (int)Math.Ceiling(length / 8.0d); sb.Append(" " + index + ": (" + length + ") ["); index++; for (var ch = 0; ch < chunkCount; ch++) { var raw = BitConverter.GetBytes(encodedTokens[index++]); sb.Append(MakeSafe(Encoding.ASCII.GetString(raw))); } sb.AppendLine("]"); } sb.AppendLine("End of data table"); } } // output remaining bytecodes for (; index < encodedTokens.Count; index++) { var token = encodedTokens[index]; var type = NanTags.TypeOf(token); sb.Append(index.ToString()); sb.Append(" "); sb.Append(type.ToString()); sb.Append(": "); sb.AppendLine(Stringify(token, type, debugSymbols)); } return(sb.ToString()); }
public void pointer_encoding_survives_a_round_trip() { var rnd = new Random(); for (int i = 0; i < 10000; i++) { long target = rnd.Next() + ((long)rnd.Next() << 15); var type = DataType.PtrArray_String; var enc = NanTags.EncodePointer(target, type); NanTags.DecodePointer(enc, out var newTarget, out var newType); Assert.That(newTarget, Is.EqualTo(target), "Multiplier: " + i); Assert.That(newType, Is.EqualTo(type)); } }
public int CastInt(double encoded) { int result; var type = NanTags.TypeOf(encoded); switch (type) { case DataType.Invalid: case DataType.Opcode: case DataType.NoValue: return(0); case DataType.VariableRef: // Follow scope var next = Variables.Resolve(NanTags.DecodeVariableRef(encoded)); return(CastInt(next)); case DataType.ValSmallString: int.TryParse(NanTags.DecodeShortStr(encoded), out result); return(result); case DataType.PtrStaticString: case DataType.PtrString: NanTags.DecodePointer(encoded, out var target, out _); int.TryParse(DereferenceString(target), out result); return(result); case DataType.PtrHashtable: case DataType.PtrGrid: case DataType.PtrSet: case DataType.PtrVector: return(0); case DataType.Number: return((int)encoded); case DataType.ValInt32: return(NanTags.DecodeInt32(encoded)); case DataType.ValUInt32: return((int)NanTags.DecodeUInt32(encoded)); default: throw new ArgumentOutOfRangeException(); } }
public string DereferenceString(long position) { // a string is [NanTag(UInt32): byte length] [string bytes, padded to 8 byte chunks] // 1) read the byte length var header = encodedTokens[(int)position]; if (NanTags.TypeOf(header) != DataType.ValUInt32) { throw new Exception("String header was not a UInt32"); } var length = NanTags.DecodeUInt32(header); // 2) calculate chunk count and read the chunks var chunkCount = (int)Math.Ceiling(length / 8.0d); var rawBytes = encodedTokens.GetRange((int)position + 1, chunkCount).SelectMany(BitConverter.GetBytes).ToArray(); // 4) make string (ascii for now, then utf-8?) return(Encoding.ASCII.GetString(rawBytes, 0, (int)length)); }
public void opcodes_survive_a_round_trip() { double enc1 = NanTags.EncodeOpcode('c', 'j', 123, 0); // control, jump, 123, <unused> double enc2 = NanTags.EncodeOpcode('f', 'd', 3, 40); // function, define, 3 params, 40 opcodes Assert.That(NanTags.TypeOf(enc1), Is.EqualTo(DataType.Opcode)); Assert.That(NanTags.TypeOf(enc2), Is.EqualTo(DataType.Opcode)); NanTags.DecodeOpCode(enc1, out var codeClass, out var codeAction, out var p1, out var p2); Assert.That(codeClass, Is.EqualTo('c')); Assert.That(codeAction, Is.EqualTo('j')); Assert.That(p1, Is.EqualTo(123)); Assert.That(p2, Is.EqualTo(0)); NanTags.DecodeOpCode(enc2, out codeClass, out codeAction, out p1, out p2); Assert.That(codeClass, Is.EqualTo('f')); Assert.That(codeAction, Is.EqualTo('d')); Assert.That(p1, Is.EqualTo(3)); Assert.That(p2, Is.EqualTo(40)); }
public string CastString(double encoded) { var type = NanTags.TypeOf(encoded); switch (type) { case DataType.Invalid: return("<invalid value>"); case DataType.Number: return(encoded.ToString(CultureInfo.InvariantCulture)); case DataType.Opcode: return("<Op Code>"); case DataType.NoValue: return(""); case DataType.VariableRef: // Follow scope var next = Variables.Resolve(NanTags.DecodeVariableRef(encoded)); return(CastString(next)); case DataType.ValSmallString: return(NanTags.DecodeShortStr(encoded)); case DataType.PtrStaticString: case DataType.PtrString: NanTags.DecodePointer(encoded, out var target, out _); return(DereferenceString(target)); case DataType.PtrHashtable: case DataType.PtrGrid: case DataType.PtrSet: case DataType.PtrVector: return("<complex type>"); case DataType.ValInt32: return(NanTags.DecodeInt32(encoded).ToString()); case DataType.ValUInt32: return(NanTags.DecodeUInt32(encoded).ToString()); default: throw new ArgumentOutOfRangeException(); } }
public void identifier_names_can_be_encoded_and_survive_a_round_trip() { var enc = NanTags.EncodeVariableRef("HelloWorld", out var crush); var type = NanTags.TypeOf(enc); var enc2 = NanTags.EncodeVariableRef("Hel" + "lo" + "Wo" + 'r' + 'l' + 'd', out var crush2); var other = NanTags.EncodeVariableRef("HelloWorld2", out var crushOther); Console.WriteLine("Crush: " + crush.ToString("X")); Console.WriteLine("Crush: " + crushOther.ToString("X")); var checkCrush = NanTags.DecodeVariableRef(enc); Assert.That(checkCrush, Is.EqualTo(crush)); Assert.That(type, Is.EqualTo(DataType.VariableRef)); Assert.That(NanTags.AreEqual(enc, enc2), Is.True); Assert.That(NanTags.AreEqual(enc, other), Is.False); Assert.That(crush, Is.EqualTo(crush2)); Assert.That(crush, Is.Not.EqualTo(crushOther)); }
private int PrepareFunctionCall(int position, ushort nbParams, Stack <double> valueStack, Stack <int> returnStack) { var functionNameHash = NanTags.DecodeVariableRef(valueStack.Pop()); var param = ReadParams(position, nbParams, valueStack); // Evaluate function. var evalResult = EvaluateFunctionCall(ref position, functionNameHash, nbParams, param, returnStack, valueStack); // Add result on stack as a value. if (NanTags.TypeOf(evalResult) != DataType.NoValue) { valueStack.Push(evalResult); } else if (NanTags.DecodeNonValue(evalResult) == NonValueType.Unit) { valueStack.Push(evalResult); } return(position); }
/// <summary> /// Symbol mapping for built-in functions /// </summary> public static HashLookup <FunctionDefinition> BuiltInFunctionSymbols() { var tmp = new HashLookup <FunctionDefinition>(64); Action <string, FuncDef> add = (name, type) => { tmp.Add(NanTags.GetCrushedName(name), new FunctionDefinition { Kind = type }); }; add("=", FuncDef.Equal); add("equals", FuncDef.Equal); add(">", FuncDef.GreaterThan); add("<", FuncDef.LessThan); add("<>", FuncDef.NotEqual); add("not-equal", FuncDef.NotEqual); add("assert", FuncDef.Assert); add("random", FuncDef.Random); add("eval", FuncDef.Eval); add("call", FuncDef.Call); add("not", FuncDef.LogicNot); add("or", FuncDef.LogicOr); add("and", FuncDef.LogicAnd); add("readkey", FuncDef.ReadKey); add("readline", FuncDef.ReadLine); add("print", FuncDef.Print); add("substring", FuncDef.Substring); add("length", FuncDef.Length); add("replace", FuncDef.Replace); add("concat", FuncDef.Concat); add("+", FuncDef.MathAdd); add("-", FuncDef.MathSub); add("*", FuncDef.MathProd); add("/", FuncDef.MathDiv); add("%", FuncDef.MathMod); add("()", FuncDef.UnitEmpty); // empty value marker return(tmp); }
public bool CastBoolean(double encoded) { var type = NanTags.TypeOf(encoded); switch (type) { case DataType.Number: return(Math.Abs(encoded) > double.Epsilon); case DataType.ValInt32: return(NanTags.DecodeInt32(encoded) != 0); case DataType.ValUInt32: return(NanTags.DecodeUInt32(encoded) != 0); case DataType.ValSmallString: { var strVal = NanTags.DecodeShortStr(encoded); return(StringTruthyness(strVal)); } case DataType.PtrString: case DataType.PtrStaticString: { NanTags.DecodePointer(encoded, out var ptr, out _); var strVal = DereferenceString(ptr); return(StringTruthyness(strVal)); } case DataType.VariableRef: // Follow scope var next = Variables.Resolve(NanTags.DecodeVariableRef(encoded)); return(CastBoolean(next)); // All the things that can't be meaningfully cast are 'false' default: return(false); } }
public void short_identifier_names_get_encoded_uniquely() { var seen = new HashSet <ulong>(); for (int i = 0; i < 60; i++) { var cs = ((char)('A' + i)).ToString(); NanTags.EncodeVariableRef(cs, out var crush); Assert.That(seen.Contains(crush), Is.False); seen.Add(crush); } for (int i = 0; i < 60; i++) { for (int j = 0; j < 10; j++) { var cs = ((char)('A' + i)).ToString() + j; NanTags.EncodeVariableRef(cs, out var crush); Assert.That(seen.Contains(crush), Is.False); seen.Add(crush); } } }
// ReSharper disable once UnusedMember.Global /// <summary> /// Start with source code, and run program to termination /// </summary> /// <param name="languageInput">Source code</param> /// <param name="input">User typing input (for readline / readkey etc)</param> /// <param name="output">Print output from the program</param> /// <param name="trace">Flag. If true, write the entire interpreter flow to output</param> /// <param name="printIL">Flag. If true, write a view of the bytecode IL to output</param> /// <param name="traceMemory">Flag. If true, write memory and GC state</param> /// <returns>Run time, excluding compile time</returns> public static TimeSpan BuildAndRun(string languageInput, TextReader input, TextWriter output, bool trace, bool printIL, bool traceMemory) { // Compile var sourceCodeReader = new SourceCodeTokeniser(); var program = sourceCodeReader.Read(languageInput, false); if (!program.IsValid) { output.WriteLine("Program is not well formed"); // TODO: be more helpful return(TimeSpan.Zero); } ToNanCodeCompiler.BaseDirectory = baseDirectory; var compiledOutput = ToNanCodeCompiler.CompileRoot(program, debug: false); // Load var stream = new MemoryStream(); compiledOutput.WriteToStream(stream); stream.Seek(0, SeekOrigin.Begin); var memoryModel = new RuntimeMemoryModel(stream); if (printIL) { output.WriteLine("======= BYTE CODE SUMMARY =========="); compiledOutput.AddSymbols(ByteCodeInterpreter.DebugSymbolsForBuiltIns()); output.WriteLine(memoryModel.ToString(compiledOutput.GetSymbols())); output.WriteLine("===================================="); } // Execute var sw = new Stopwatch(); var interpreter = new ByteCodeInterpreter(); try { // Init the interpreter. interpreter.Init(memoryModel, input, output, debugSymbols: compiledOutput.GetSymbols()); sw.Start(); interpreter.Execute(false, trace, false); sw.Stop(); if (traceMemory) { output.WriteLine("======== GARBAGE SUMMARY ==========="); foreach (var token in memoryModel.Variables.PotentialGarbage) { output.WriteLine(NanTags.Describe(token)); } output.WriteLine("===================================="); } return(sw.Elapsed); } catch (Exception e) { output.WriteLine("Interpreter stopped at " + interpreter.LastPosition()); output.WriteLine("Exception : " + e.Message); output.WriteLine("\r\n\r\n" + e.StackTrace); sw.Stop(); return(sw.Elapsed); } }
/// <summary> /// Output atom values /// </summary> private static void EmitLeafNode(Node root, bool debug, Scope parameterNames, Context compileContext, NanCodeWriter wr) { double leafValue = 0; bool substitute = false; var valueName = root.Text; var nameHash = NanTags.GetCrushedName(valueName); if (parameterNames.CanResolve(nameHash)) { substitute = true; leafValue = parameterNames.Resolve(nameHash); } // An unwrapped variable name? if (IsUnwrappedIdentifier(valueName, root, compileContext)) { if (debug) { wr.Comment("// treating '" + valueName + "' as an implicit get()"); } if (substitute) { wr.Memory('g', leafValue); } else { wr.Memory('g', valueName, 0); } return; } if (debug) { wr.Comment("// Value : \"" + root + "\"\r\n"); if (substitute) { wr.Comment("// Parameter reference redefined as '" + valueName + "'\r\n"); } } switch (root.NodeType) { case NodeType.Numeric: wr.LiteralNumber(double.Parse(valueName.Replace("_", ""))); break; case NodeType.StringLiteral: wr.LiteralString(valueName); break; case NodeType.Atom: if (valueName == "true") { wr.LiteralInt32(-1); } else if (valueName == "false") { wr.LiteralInt32(0); } else if (substitute) { wr.RawToken(leafValue); } else { wr.VariableReference(valueName); } break; default: throw new Exception("Unexpected compiler state"); } }
public string DiagnosticString(double token, HashLookup <string> symbols = null) { return(Stringify(token, NanTags.TypeOf(token), symbols)); }
/// <summary> /// Main loop. /// Good default for all the flags is `false` /// </summary> /// <param name="resetVars">If true, all scopes are DELETED before running</param> /// <param name="traceExecution">If true, console output of state is written</param> /// <param name="singleStep">If true, the interpreter will run a single step, then return. Internal `eval` statements will run to completion</param> public ExecutionResult Execute(bool resetVars, bool traceExecution, bool singleStep) { double evalResult; _runningVerbose = traceExecution; if (resetVars) { _memory.Variables.Clear(); } while (_position < program.Count) { _stepsTaken++; //if (stepsTaken > 1000) throw new Exception("trap"); // Prevent stackoverflow. // Ex: if(true 1 10 20) if ((_stepsTaken & 127) == 0 && _valueStack.Count > 100) { var oldValues = _valueStack.ToArray(); _valueStack = new Stack <double>(oldValues.Skip(oldValues.Length - 100)); } double word = program[_position]; if (traceExecution) { _output.WriteLine(" stack :" + string.Join(", ", _valueStack.ToArray().Select(t => _memory.DiagnosticString(t, DebugSymbols)))); _output.WriteLine(" #" + _stepsTaken + "; p=" + _position + "; w=" + _memory.DiagnosticString(word, DebugSymbols)); } var type = NanTags.TypeOf(word); switch (type) { case DataType.Invalid: throw new Exception("Unknown code point at " + _position); case DataType.Opcode: // decode opcode and do stuff NanTags.DecodeOpCode(word, out var codeClass, out var codeAction, out var p1, out var p2); ProcessOpCode(codeClass, codeAction, p1, p2, ref _position, _valueStack, _returnStack, word); break; default: _valueStack.Push(word); // these could be raw doubles, encoded real values, or references/pointers break; } _position++; if (singleStep) { return new ExecutionResult { State = ExecutionState.Paused, Result = NanTags.EncodeNonValue(NonValueType.Not_a_Result) } } ; } if (_valueStack.Count != 0) { evalResult = _valueStack.Pop(); } else { evalResult = NanTags.EncodeNonValue(NonValueType.Void); } _valueStack.Clear(); return(new ExecutionResult { State = ExecutionState.Complete, Result = evalResult }); }