Example #1
0
        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);
            }
        }
Example #2
0
        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");
            }
        }
Example #3
0
        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));
            }
        }
Example #4
0
        /// <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());
        }
Example #5
0
        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();
            }
        }
Example #6
0
        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));
        }
Example #7
0
        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));
        }
Example #8
0
        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();
            }
        }
Example #9
0
        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);
        }
Example #10
0
        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));
        }
Example #11
0
        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);
            }
        }
Example #12
0
 public string DiagnosticString(double token, HashLookup <string> symbols = null)
 {
     return(Stringify(token, NanTags.TypeOf(token), symbols));
 }
Example #13
0
        /// <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
            });
        }
Example #14
0
        /// <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();
            }
        }