示例#1
0
        public static ValList Create(int capacity = 0)
        {
            //Console.WriteLine("ValList create cap = " + capacity + " ID " + _num);
            if (_valuePool == null)
            {
                _valuePool = new ValuePool <ValList>();
            }
            else
            {
                ValList existing = _valuePool.GetInstance();
                if (existing != null)
                {
                    existing._refCount = 1;
                    existing.EnsureCapacity(capacity);
#if MINISCRIPT_DEBUG
                    existing._id = _num++;
#endif
                    return(existing);
                }
            }

#if MINISCRIPT_DEBUG
            _numInstancesAllocated++;
#endif
            return(new ValList(capacity, true));
        }
示例#2
0
        public ValList EvalCopy(TAC.Context context)
        {
            // Create a copy of this list, evaluating its members as we go.
            // This is used when a list literal appears in the source, to
            // ensure that each time that code executes, we get a new, distinct
            // mutable object, rather than the same object multiple times.
            var result = new ValList();

            for (var i = 0; i < values.Count; i++)
            {
                result.values.Add(values[i] == null ? null : values[i].Val(context));
            }
            return(result);
        }
示例#3
0
        public ValList EvalCopy(Context context)
        {
            // Create a copy of this list, evaluating its members as we go.
            // This is used when a list literal appears in the source, to
            // ensure that each time that code executes, we get a new, distinct
            // mutable object, rather than the same object multiple times.
            var result = ValList.Create(values.Count);

            for (var i = StartIndex; i < values.Count; i++)
            {
                // Sometimes Val is a ValTemp that returns a value that should be reffed
                // so we Val without Refing, then Ref during Add
                result.Add(values[i] == null ? null : values[i].Val(context, false), true);
            }
            return(result);
        }
示例#4
0
        public override Value FullEval(TAC.Context context)
        {
            // Evaluate each of our list elements, and if any of those is
            // a variable or temp, then resolve those now.
            // CAUTION: do not mutate our original list!  We may need
            // it in its original form on future iterations.
            ValList result = null;

            for (int i = 0; i < values.Count; i++)
            {
                bool copied = false;
                if (values[i] is ValTemp || values[i] is ValVar)
                {
                    Value newVal = values[i].Val(context);
                    if (newVal != values[i])
                    {
                        // OK, something changed, so we're going to need a new copy of the list.
                        if (result == null)
                        {
                            result = new ValList();
                            for (int j = 0; j < i; j++)
                            {
                                result.values.Add(values[j]);
                            }
                        }
                        result.values.Add(newVal);
                        copied = true;
                    }
                }
                if (!copied && result != null)
                {
                    // No change; but we have new results to return, so copy it as-is
                    result.values.Add(values[i]);
                }
            }
            if (result != null)
            {
                return(result);
            }
            return(this);
        }
示例#5
0
        public override double Equality(Value rhs, int recursionDepth = 16)
        {
            if (!(rhs is ValList))
            {
                return(0);
            }
            ValList      rList  = rhs as ValList;
            List <Value> rhl    = rList.values;
            int          rStart = rList.StartIndex;

            if (rhl == values)
            {
                return(1);                            // (same list)
            }
            int count = Count;

            if (count != rhl.Count)
            {
                return(0);
            }
            if (recursionDepth < 1)
            {
                return(0.5);                                            // in too deep
            }
            double result = 1;

            for (var i = 0; i < Count; i++)
            {
                result *= values[StartIndex + i].Equality(rhl[rStart + i], recursionDepth - 1);
                if (result <= 0)
                {
                    break;
                }
            }
            return(result);
        }
示例#6
0
        /// <summary>
        /// Evaluate this line and return the value that would be stored
        /// into the lhs.
        /// </summary>
        public Value Evaluate(Context context)
        {
            int rhsATypeInt = rhsA == null ? -1 : rhsA.GetBaseMiniscriptType();

            if (op == Op.AssignA || op == Op.ReturnA || op == Op.AssignImplicit)
            {
                // Assignment is a bit of a special case.  It's EXTREMELY common
                // in TAC, so needs to be efficient, but we have to watch out for
                // the case of a RHS that is a list or map.  This means it was a
                // literal in the source, and may contain references that need to
                // be evaluated now.
                //if (rhsA is ValList || rhsA is ValMap) {
                if (rhsATypeInt == MiniscriptTypeInts.ValListTypeInt || rhsATypeInt == MiniscriptTypeInts.ValMapTypeInt)
                {
                    return(rhsA.FullEval(context));
                }
                else if (rhsA == null)
                {
                    return(null);
                }
                else
                {
                    return(rhsA.Val(context, true));
                }
            }
            if (op == Op.CopyA)
            {
                // This opcode is used for assigning a literal.  We actually have
                // to copy the literal, in the case of a mutable object like a
                // list or map, to ensure that if the same code executes again,
                // we get a new, unique object.
                //if (rhsA is ValList) {
                if (rhsATypeInt == MiniscriptTypeInts.ValListTypeInt)
                {
                    return(((ValList)rhsA).EvalCopy(context));
                    //} else if (rhsA is ValMap) {
                }
                else if (rhsATypeInt == MiniscriptTypeInts.ValMapTypeInt)
                {
                    return(((ValMap)rhsA).EvalCopy(context));
                }
                else if (rhsA == null)
                {
                    return(null);
                }
                else
                {
                    return(rhsA.Val(context, true));
                }
            }

            Value opA = rhsA != null?rhsA.Val(context, false) : null;

            Value opB = rhsB != null?rhsB.Val(context, false) : null;

            int opATypeInt = opA == null ? -1 : opA.GetBaseMiniscriptType();
            int opBTypeInt = opB == null ? -1 : opB.GetBaseMiniscriptType();

            if (op == Op.AisaB)
            {
                if (opA == null)
                {
                    return(ValNumber.Truth(opB == null));
                }
                return(ValNumber.Truth(opA.IsA(opB, context.vm)));
            }

            //if (op == Op.ElemBofA && opB is ValString) {
            if (op == Op.ElemBofA && opBTypeInt == MiniscriptTypeInts.ValStringTypeInt)
            {
                // You can now look for a string in almost anything...
                // and we have a convenient (and relatively fast) method for it:
                ValMap ignored;
                return(ValSeqElem.Resolve(opA, ((ValString)opB).value, context, out ignored));
            }

            // check for special cases of comparison to null (works with any type)
            if (op == Op.AEqualB && (opA == null || opB == null))
            {
                return(ValNumber.Truth(opA == opB));
            }
            if (op == Op.ANotEqualB && (opA == null || opB == null))
            {
                return(ValNumber.Truth(opA != opB));
            }

            // check for implicit coersion of other types to string; this happens
            // when either side is a string and the operator is addition.
            //if ((opA is ValString || opB is ValString) && op == Op.APlusB) {
            if ((opATypeInt == MiniscriptTypeInts.ValStringTypeInt || opBTypeInt == MiniscriptTypeInts.ValStringTypeInt) && op == Op.APlusB)
            {
                string sA = opA != null?opA.ToString(context.vm) : string.Empty;

                string sB = opB != null?opB.ToString(context.vm) : string.Empty;

                if (sA.Length + sB.Length > ValString.maxSize)
                {
                    throw new LimitExceededException("string too large");
                }
                return(ValString.Create(sA + sB));
            }

            //if (opA is ValNumber) {
            if (opATypeInt == MiniscriptTypeInts.ValNumberTypeInt)
            {
                double numA = ((ValNumber)opA).value;
                switch (op)
                {
                case Op.GotoA:
                    context.lineNum = (int)numA;
                    return(null);

                case Op.GotoAifB:
                    if (opB != null && opB.BoolValue())
                    {
                        context.lineNum = (int)numA;
                    }
                    return(null);

                case Op.GotoAifTrulyB:
                {
                    // Unlike GotoAifB, which branches if B has any nonzero
                    // value (including 0.5 or 0.001), this branches only if
                    // B is TRULY true, i.e., its integer value is nonzero.
                    // (Used for short-circuit evaluation of "or".)
                    int i = 0;
                    if (opB != null)
                    {
                        i = opB.IntValue();
                    }
                    if (i != 0)
                    {
                        context.lineNum = (int)numA;
                    }
                    return(null);
                }

                case Op.GotoAifNotB:
                    if (opB == null || !opB.BoolValue())
                    {
                        context.lineNum = (int)numA;
                    }
                    return(null);

                case Op.CallIntrinsicA:
                    // NOTE: intrinsics do not go through NextFunctionContext.  Instead
                    // they execute directly in the current context.  (But usually, the
                    // current context is a wrapper function that was invoked via
                    // Op.CallFunction, so it got a parameter context at that time.)
                    Intrinsic.Result result = Intrinsic.Execute((int)numA, context, context.partialResult);
                    if (result.done)
                    {
                        context.partialResult = default(Intrinsic.Result);
                        return(result.result);
                    }
                    // OK, this intrinsic function is not yet done with its work.
                    // We need to stay on this same line and call it again with
                    // the partial result, until it reports that its job is complete.
                    context.partialResult = result;
                    context.lineNum--;
                    return(null);

                case Op.NotA:
                    return(ValNumber.Create(1.0 - AbsClamp01(numA)));
                }
                //if (opB is ValNumber || opB == null) {
                if (opBTypeInt == MiniscriptTypeInts.ValNumberTypeInt || opB == null)
                {
                    double numB = opB != null ? ((ValNumber)opB).value : 0;
                    switch (op)
                    {
                    case Op.APlusB:
                        return(ValNumber.Create(numA + numB));

                    case Op.AMinusB:
                        return(ValNumber.Create(numA - numB));

                    case Op.ATimesB:
                        return(ValNumber.Create(numA * numB));

                    case Op.ADividedByB:
                        return(ValNumber.Create(numA / numB));

                    case Op.AModB:
                        return(ValNumber.Create(numA % numB));

                    case Op.APowB:
                        return(ValNumber.Create(Math.Pow(numA, numB)));

                    case Op.AEqualB:
                        return(ValNumber.Truth(numA == numB));

                    case Op.ANotEqualB:
                        return(ValNumber.Truth(numA != numB));

                    case Op.AGreaterThanB:
                        return(ValNumber.Truth(numA > numB));

                    case Op.AGreatOrEqualB:
                        return(ValNumber.Truth(numA >= numB));

                    case Op.ALessThanB:
                        return(ValNumber.Truth(numA < numB));

                    case Op.ALessOrEqualB:
                        return(ValNumber.Truth(numA <= numB));

                    case Op.AAndB:
                        //if (!(opB is ValNumber))
                        //numB = opB != null && opB.BoolValue() ? 1 : 0;
                        return(ValNumber.Create(Clamp01(numA * numB)));

                    case Op.AOrB:
                        //if (!(opB is ValNumber))
                        //numB = opB != null && opB.BoolValue() ? 1 : 0;
                        return(ValNumber.Create(Clamp01(numA + numB - numA * numB)));

                    default:
                        break;
                    }
                }
                else if (opBTypeInt == MiniscriptTypeInts.ValCustomTypeInt)
                {
                    // Most types should commute with number
                    // But in case not we have a flag to say if the other is on
                    // the lhs or rhs
                    ValCustom customB = opB as ValCustom;
                    switch (op)
                    {
                    case Op.APlusB:
                        return(customB.APlusB(opA, opATypeInt, context, false));

                    case Op.AMinusB:
                        return(customB.AMinusB(opA, opATypeInt, context, false));

                    case Op.ATimesB:
                        return(customB.ATimesB(opA, opATypeInt, context, false));

                    case Op.ADividedByB:
                        return(customB.ADividedByB(opA, opATypeInt, context, false));
                    }
                }
                // Handle equality testing between a number (opA) and a non-number (opB).
                // These are always considered unequal.
                if (op == Op.AEqualB)
                {
                    return(ValNumber.zero);
                }
                if (op == Op.ANotEqualB)
                {
                    return(ValNumber.one);
                }
                //} else if (opA is ValString) {
            }
            else if (opATypeInt == MiniscriptTypeInts.ValStringTypeInt)
            {
                string strA = ((ValString)opA).value;
                if (op == Op.ATimesB || op == Op.ADividedByB)
                {
                    double factor = 0;
                    if (op == Op.ATimesB)
                    {
                        Check.Type(opB, typeof(ValNumber), "string replication");
                        factor = ((ValNumber)opB).value;
                    }
                    else
                    {
                        Check.Type(opB, typeof(ValNumber), "string division");
                        factor = 1.0 / ((ValNumber)opB).value;
                    }
                    int repeats = (int)factor;
                    if (repeats < 0)
                    {
                        return(ValString.empty);
                    }
                    if (repeats * strA.Length > ValString.maxSize)
                    {
                        throw new LimitExceededException("string too large");
                    }
                    if (_workingStringBuilder == null)
                    {
                        _workingStringBuilder = new StringBuilder();
                    }
                    else
                    {
                        _workingStringBuilder.Clear();
                    }
                    for (int i = 0; i < repeats; i++)
                    {
                        _workingStringBuilder.Append(strA);
                    }
                    int extraChars = (int)(strA.Length * (factor - repeats));
                    if (extraChars > 0)
                    {
                        _workingStringBuilder.Append(strA.Substring(0, extraChars));
                    }
                    return(ValString.Create(_workingStringBuilder.ToString()));
                }
                if (op == Op.ElemBofA || op == Op.ElemBofIterA)
                {
                    int idx = opB.IntValue();
                    Check.Range(idx, -strA.Length, strA.Length - 1, "string index");
                    if (idx < 0)
                    {
                        idx += strA.Length;
                    }
                    return(ValString.Create(strA.Substring(idx, 1)));
                }
                //if (opB == null || opB is ValString) {
                if (opB == null || opBTypeInt == MiniscriptTypeInts.ValStringTypeInt)
                {
                    string sB = (opB == null ? null : opB.ToString(context.vm));
                    switch (op)
                    {
                    case Op.AMinusB: {
                        if (opB == null)
                        {
                            opA.Ref();
                            return(opA);
                        }
                        if (strA.EndsWith(sB))
                        {
                            strA = strA.Substring(0, strA.Length - sB.Length);
                        }
                        return(ValString.Create(strA));
                    }

                    case Op.NotA:
                        return(ValNumber.Truth(string.IsNullOrEmpty(strA)));

                    case Op.AEqualB:
                        return(ValNumber.Truth(string.Equals(strA, sB)));

                    case Op.ANotEqualB:
                        return(ValNumber.Truth(!string.Equals(strA, sB)));

                    case Op.AGreaterThanB:
                        return(ValNumber.Truth(string.Compare(strA, sB, StringComparison.Ordinal) > 0));

                    case Op.AGreatOrEqualB:
                        return(ValNumber.Truth(string.Compare(strA, sB, StringComparison.Ordinal) >= 0));

                    case Op.ALessThanB:
                        int foo = string.Compare(strA, sB, StringComparison.Ordinal);
                        return(ValNumber.Truth(foo < 0));

                    case Op.ALessOrEqualB:
                        return(ValNumber.Truth(string.Compare(strA, sB, StringComparison.Ordinal) <= 0));

                    case Op.LengthOfA:
                        return(ValNumber.Create(strA.Length));

                    default:
                        break;
                    }
                }
                else
                {
                    // RHS is neither null nor a string.
                    // We no longer automatically coerce in all these cases; about
                    // all we can do is equal or unequal testing.
                    // (Note that addition was handled way above here.)
                    if (op == Op.AEqualB)
                    {
                        return(ValNumber.zero);
                    }
                    if (op == Op.ANotEqualB)
                    {
                        return(ValNumber.one);
                    }
                }
                //} else if (opA is ValList) {
            }
            else if (opATypeInt == MiniscriptTypeInts.ValListTypeInt)
            {
                ValList listA = ((ValList)opA);
                if (op == Op.ElemBofA || op == Op.ElemBofIterA)
                {
                    // list indexing
                    int idx = opB.IntValue();
                    Check.Range(idx, -listA.Count, listA.Count - 1, "list index");
                    if (idx < 0)
                    {
                        idx += listA.Count;
                    }
                    Value val = listA[idx];
                    if (val != null)
                    {
                        val.Ref();
                    }
                    return(val);
                }
                else if (op == Op.LengthOfA)
                {
                    return(ValNumber.Create(listA.Count));
                }
                else if (op == Op.AEqualB)
                {
                    return(ValNumber.Truth(((ValList)opA).Equality(opB)));
                }
                else if (op == Op.ANotEqualB)
                {
                    return(ValNumber.Truth(1.0 - ((ValList)opA).Equality(opB)));
                }
                else if (op == Op.APlusB)
                {
                    // list concatenation
                    Check.Type(opB, typeof(ValList), "list concatenation");
                    ValList listB = ((ValList)opB);
                    if (listA.Count + listB.Count > ValList.maxSize)
                    {
                        throw new LimitExceededException("list too large");
                    }
                    ValList result = ValList.Create(listA.Count + listB.Count);
                    for (int i = 0; i < listA.Count; i++)
                    {
                        result.Add(context.ValueInContext(listA[i]));
                    }
                    for (int i = 0; i < listB.Count; i++)
                    {
                        result.Add(context.ValueInContext(listB[i]));
                    }
                    return(result);
                }
                else if (op == Op.ATimesB || op == Op.ADividedByB)
                {
                    // list replication (or division)
                    double factor = 0;
                    if (op == Op.ATimesB)
                    {
                        Check.Type(opB, typeof(ValNumber), "list replication");
                        factor = ((ValNumber)opB).value;
                    }
                    else
                    {
                        Check.Type(opB, typeof(ValNumber), "list division");
                        factor = 1.0 / ((ValNumber)opB).value;
                    }
                    if (factor <= 0)
                    {
                        return(ValList.Create());
                    }
                    int finalCount = (int)(listA.Count * factor);
                    if (finalCount > ValList.maxSize)
                    {
                        throw new LimitExceededException("list too large");
                    }
                    ValList result = ValList.Create(finalCount);
                    for (int i = 0; i < finalCount; i++)
                    {
                        result.Add(listA[i % listA.Count]);
                    }
                    return(result);
                }
                else if (op == Op.NotA)
                {
                    return(ValNumber.Truth(!opA.BoolValue()));
                }
                //} else if (opA is ValMap) {
            }
            else if (opATypeInt == MiniscriptTypeInts.ValMapTypeInt)
            {
                if (op == Op.ElemBofA)
                {
                    // map lookup
                    // (note, cases where opB is a string are handled above, along with
                    // all the other types; so we'll only get here for non-string cases)
                    ValSeqElem se  = ValSeqElem.Create(opA, opB);
                    Value      ret = se.Val(context, true);
                    //if (se != null)
                    //se.Unref();
                    return(ret);
                    // (This ensures we walk the "__isa" chain in the standard way.)
                }
                else if (op == Op.ElemBofIterA)
                {
                    // With a map, ElemBofIterA is different from ElemBofA.  This one
                    // returns a mini-map containing a key/value pair.
                    return(((ValMap)opA).GetKeyValuePair(opB.IntValue()));
                }
                else if (op == Op.LengthOfA)
                {
                    return(ValNumber.Create(((ValMap)opA).Count));
                }
                else if (op == Op.AEqualB)
                {
                    return(ValNumber.Truth(((ValMap)opA).Equality(opB)));
                }
                else if (op == Op.ANotEqualB)
                {
                    return(ValNumber.Truth(1.0 - ((ValMap)opA).Equality(opB)));
                }
                else if (op == Op.APlusB)
                {
                    // map combination
                    Check.Type(opB, typeof(ValMap), "map combination");
                    ValMap result = ValMap.Create();
                    ValMap mapA   = opA as ValMap;
                    var    aKeys  = mapA.Keys;
                    var    aVals  = mapA.Values;
                    for (int i = 0; i < aKeys.Count; i++)
                    {
                        result[aKeys[i]] = context.ValueInContext(aVals[i]);
                    }
                    ValMap mapB  = opB as ValMap;
                    var    bKeys = mapB.Keys;
                    var    bVals = mapB.Values;
                    for (int i = 0; i < bKeys.Count; i++)
                    {
                        result[bKeys[i]] = context.ValueInContext(bVals[i]);
                    }
                    return(result);
                }
                else if (op == Op.NotA)
                {
                    return(ValNumber.Truth(!opA.BoolValue()));
                }
                //} else if (opA is ValFunction && opB is ValFunction) {
            }
            else if (opATypeInt == MiniscriptTypeInts.ValFunctionTypeInt && opBTypeInt == MiniscriptTypeInts.ValFunctionTypeInt)
            {
                Function fA = ((ValFunction)opA).function;
                Function fB = ((ValFunction)opB).function;
                switch (op)
                {
                case Op.AEqualB:
                    return(ValNumber.Truth(fA == fB));

                case Op.ANotEqualB:
                    return(ValNumber.Truth(fA != fB));
                }
            }
            else if (opATypeInt == MiniscriptTypeInts.ValCustomTypeInt)
            {
                ValCustom customA = opA as ValCustom;
                switch (op)
                {
                case Op.APlusB:
                    return(customA.APlusB(opB, opBTypeInt, context, true));

                case Op.AMinusB:
                    return(customA.AMinusB(opB, opBTypeInt, context, true));

                case Op.ATimesB:
                    return(customA.ATimesB(opB, opBTypeInt, context, true));

                case Op.ADividedByB:
                    return(customA.ADividedByB(opB, opBTypeInt, context, true));
                }
            }
            else
            {
                // opA is something else... perhaps null
                switch (op)
                {
                case Op.BindAssignA:
                {
                    if (context.variables == null)
                    {
                        context.variables = ValMap.Create();
                    }
                    ValFunction valFunc = (ValFunction)opA;
                    return(valFunc.BindAndCopy(context.variables));
                    //valFunc.outerVars = context.variables;
                    //return null;
                }

                case Op.NotA:
                    return(opA != null && opA.BoolValue() ? ValNumber.zero : ValNumber.one);
                }
            }


            if (op == Op.AAndB || op == Op.AOrB)
            {
                // We already handled the case where opA was a number above;
                // this code handles the case where opA is something else.
                double numA = opA != null && opA.BoolValue() ? 1 : 0;
                double numB;
                //if (opB is ValNumber) fB = ((ValNumber)opB).value;
                if (opBTypeInt == MiniscriptTypeInts.ValNumberTypeInt)
                {
                    numB = ((ValNumber)opB).value;
                }
                else
                {
                    numB = opB != null && opB.BoolValue() ? 1 : 0;
                }
                double result;
                if (op == Op.AAndB)
                {
                    result = numA * numB;
                }
                else
                {
                    result = 1.0 - (1.0 - AbsClamp01(numA)) * (1.0 - AbsClamp01(numB));
                }
                return(ValNumber.Create(result));
            }
            return(null);
        }