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 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 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> /// 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 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(); } }
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); }
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)); }
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 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 }); }
/// <summary> /// First param is compared all others. If *any* are equal, the function returns true. /// </summary> private bool ListEquals(double[] list) { var type = NanTags.TypeOf(list[0]); switch (type) { // Non-comparable types case DataType.Invalid: case DataType.NoValue: case DataType.Opcode: return(false); // Numeric types case DataType.Number: case DataType.ValInt32: case DataType.ValUInt32: { var target = _memory.CastDouble(list[0]); for (int i = 1; i < list.Length; i++) { if (Math.Abs(target - _memory.CastDouble(list[i])) <= ComparisonPrecision) { return(true); } } return(false); } // String types case DataType.ValSmallString: case DataType.PtrStaticString: case DataType.PtrString: { var target = _memory.CastString(list[0]); for (int i = 1; i < list.Length; i++) { if (target == _memory.CastString(list[i])) { return(true); } } return(false); } // Pointer equality case DataType.VariableRef: case DataType.PtrHashtable: case DataType.PtrGrid: case DataType.PtrSet: case DataType.PtrVector: { var target = NanTags.DecodeRaw(list[0]); for (int i = 1; i < list.Length; i++) { if (target == NanTags.DecodeRaw(list[i])) { return(true); } } return(false); } default: throw new ArgumentOutOfRangeException(); } }