Beispiel #1
0
        /// <summary>
        /// Reset this context to the first line of code, clearing out any
        /// temporary variables, and optionally clearing out all variables.
        /// </summary>
        /// <param name="clearVariables">if true, clear our local variables</param>
        public void Reset(bool clearVariables = true)
        {
            lineNum = 0;
            // #0 is the return variable, which we don't want to unref
            // unless this is the root context, in which case we unref it all
            int start = root == this ? 0 : 1;

            for (int i = start; i < temps.Count; i++)
            {
                TempEntry entry = temps[i];
                if (!entry.Unref)
                {
                    continue;
                }
                entry.value?.Unref();
            }
            temps.Clear();
            if (clearVariables)
            {
                if (variables != null)
                {
                    variables.Unref();
                }
                variables = null;
            }
        }
Beispiel #2
0
        public ValMap EvalCopy(Context context)
        {
            // Create a copy of this map, evaluating its members as we go.
            // This is used when a map 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 = ValMap.Create();
            var keys   = Keys;
            var values = Values;

            for (int i = 0; i < keys.Count; i++)
            {
                Value key   = keys[i];
                Value value = values[i];
                if (key is ValTemp || key is ValVar)
                {
                    key = key.Val(context, false);
                }
                if (value is ValTemp || value is ValVar)
                {
                    value = value.Val(context, false);
                }
                result.SetElem(key, value, true, true);
            }
            return(result);
        }
Beispiel #3
0
        /// <summary>
        /// Look up a value in this dictionary, walking the __isa chain to find
        /// it in a parent object if necessary; return both the value found and
        /// (via the output parameter) the map it was found in.
        /// </summary>
        /// <param name="key">key to search for</param>
        /// <returns>value associated with that key, or null if not found</returns>
        public Value Lookup(Value key, out ValMap valueFoundIn)
        {
            if (key == null)
            {
                key = ValNull.instance;
            }
            Value  result = null;
            ValMap obj    = this;

            while (obj != null)
            {
                if (obj.map.TryGetValue(key, out result))
                {
                    valueFoundIn = obj;
                    return(result);
                }
                Value parent;
                if (!obj.map.TryGetValue(ValString.magicIsA, out parent))
                {
                    break;
                }
                obj = parent as ValMap;
            }
            valueFoundIn = null;
            return(null);
        }
Beispiel #4
0
        public override Value Val(TAC.Context context, out ValMap valueFoundIn)
        {
            valueFoundIn = null;
            Value idxVal = index == null ? null : index.Val(context);

            if (idxVal is ValString)
            {
                return(Resolve(sequence, ((ValString)idxVal).value, context, out valueFoundIn));
            }
            // Ok, we're searching for something that's not a string;
            // this can only be done in maps and lists (and lists, only with a numeric index).
            Value baseVal = sequence.Val(context);

            if (baseVal is ValMap)
            {
                Value result = ((ValMap)baseVal).Lookup(idxVal, out valueFoundIn);
                if (valueFoundIn == null)
                {
                    throw new KeyException(idxVal.CodeForm(context.vm, 1));
                }
                return(result);
            }
            else if (baseVal is ValList && idxVal is ValNumber)
            {
                return(((ValList)baseVal).GetElem(idxVal));
            }
            else if (baseVal is ValString && idxVal is ValNumber)
            {
                return(((ValString)baseVal).GetElem(idxVal));
            }

            throw new TypeException("Type Exception: can't index into this type");
        }
Beispiel #5
0
 /// <summary>
 /// Reset this context to the first line of code, clearing out any
 /// temporary variables, and optionally clearing out all variables.
 /// </summary>
 /// <param name="clearVariables">if true, clear our local variables</param>
 public void Reset(bool clearVariables = true)
 {
     lineNum = 0;
     temps   = null;
     if (clearVariables)
     {
         variables = new ValMap();
     }
 }
Beispiel #6
0
 public override Value Val(TAC.Context context, out ValMap valueFoundIn)
 {
     valueFoundIn = null;
     if (this == self)
     {
         return(context.self);
     }
     return(context.GetVar(identifier));
 }
Beispiel #7
0
 public void SetVar(Value identifier, Value value)
 {
     if (variables == null)
     {
         variables = ValMap.Create();
     }
     if (variables.assignOverride == null || !variables.assignOverride(identifier, value))
     {
         variables.SetElem(identifier, value, false);
     }
 }
Beispiel #8
0
 public void SetVar(string identifier, Value value)
 {
     if (identifier == "globals" || identifier == "locals")
     {
         throw new RuntimeException("can't assign to " + identifier);
     }
     if (variables == null)
     {
         variables = new ValMap();
     }
     variables[identifier] = value;
 }
Beispiel #9
0
            /// <summary>
            /// Get the value of a variable available in this context (including
            /// locals, globals, and intrinsics).  Raise an exception if no such
            /// identifier can be found.
            /// </summary>
            /// <param name="identifier">name of identifier to look up</param>
            /// <returns>value of that identifier</returns>
            public Value GetVar(string identifier)
            {
                // check for special built-in identifiers 'locals' and 'globals'
                if (identifier == "locals")
                {
                    if (variables == null)
                    {
                        variables = new ValMap();
                    }
                    return(variables);
                }
                if (identifier == "globals")
                {
                    if (root.variables == null)
                    {
                        root.variables = new ValMap();
                    }
                    return(root.variables);
                }

                // check for a local variable
                Value result;

                if (variables != null && variables.TryGetValue(identifier, out result))
                {
                    return(result);
                }

                // OK, we don't have a local variable with that name.
                // Check higher scopes.
                Context c = parent;

                while (c != null)
                {
                    if (c.variables != null && c.variables.ContainsKey(identifier))
                    {
                        return(c.variables[identifier]);
                    }
                    c = c.parent;
                }

                // Finally, check intrinsics.
                Intrinsic intrinsic = Intrinsic.GetByName(identifier);

                if (intrinsic != null)
                {
                    return(intrinsic.GetFunc());
                }

                // No luck there either?  Undefined identifier.
                throw new UndefinedIdentifierException(identifier);
            }
Beispiel #10
0
        public override double Equality(Value rhs, int recursionDepth = 16)
        {
            if (!(rhs is ValMap))
            {
                return(0);
            }
            ValMap rhm = rhs as ValMap;

            if (rhm == this)
            {
                return(1);                          // (same map)
            }
            if (Count != rhm.Count)
            {
                return(0);
            }
            if (recursionDepth < 1)
            {
                return(0.5);                                            // in too deep
            }
            double result    = 1;
            var    ourKeys   = Keys;
            var    ourVals   = Values;
            var    theirKeys = rhm.Keys;
            var    theirVals = rhm.Values;

            for (int i = 0; i < ourKeys.Count; i++)
            {
                if (ourKeys[i] != theirKeys[i])
                {
                    return(0);
                }
                Value ourVal   = ourVals[i];
                Value theirVal = theirVals[i];
                if (ourVal == null && theirVal != null)
                {
                    return(0);
                }
                if (ourVal == null && theirVal == null)
                {
                    continue;
                }
                result *= ourVal.Equality(theirVal, recursionDepth - 1);
                if (result <= 0)
                {
                    break;
                }
            }
            return(result);
        }
Beispiel #11
0
        /// <summary>
        /// Get the indicated key/value pair as another map containing "key" and "value".
        /// (This is used when iterating over a map with "for".)
        /// </summary>
        /// <param name="index">0-based index of key/value pair to get.</param>
        /// <returns>new map containing "key" and "value" with the requested key/value pair</returns>
        public ValMap GetKeyValuePair(int index)
        {
            Dictionary <Value, Value> .KeyCollection keys = map.Keys;
            if (index < 0 || index >= keys.Count)
            {
                throw new IndexException("index " + index + " out of range for map");
            }
            Value key    = keys.ElementAt <Value>(index);           // (TODO: consider more efficient methods here)
            var   result = new ValMap();

            result.map[keyStr] = (key is ValNull ? null : key);
            result.map[valStr] = map[key];
            return(result);
        }
Beispiel #12
0
        /// <summary>
        /// Get the indicated key/value pair as another map containing "key" and "value".
        /// (This is used when iterating over a map with "for".)
        /// </summary>
        /// <param name="index">0-based index of key/value pair to get.</param>
        /// <returns>new map containing "key" and "value" with the requested key/value pair</returns>
        public ValMap GetKeyValuePair(int index)
        {
            var keys = Keys;

            if (index < 0 || index >= keys.Count)
            {
                throw new IndexException("index " + index + " out of range for map");
            }
            var val    = Values[index];
            var key    = keys[index];
            var result = ValMap.Create();

            result.SetElem(keyStr, (key is ValNull) ? null : key, true, true);
            result.SetElem(valStr, val, true, true);
            return(result);
        }
        public override Value Val(Context context, out ValMap valueFoundIn)
        {
            valueFoundIn = null;
            Value     idxVal    = index == null ? null : index.Val(context, false);
            ValString idxValStr = idxVal as ValString;

            if (idxValStr != null)
            {
                return(Resolve(sequence, idxValStr.value, context, out valueFoundIn));
            }
            // Ok, we're searching for something that's not a string;
            // this can only be done in maps and lists (and lists, only with a numeric index).
            Value baseVal        = sequence.Val(context, false);
            int   baseValTypeInt = baseVal.GetBaseMiniscriptType();

            if (baseValTypeInt == MiniscriptTypeInts.ValMapTypeInt)
            {
                Value result = ((ValMap)baseVal).Lookup(idxVal, out valueFoundIn);
                if (valueFoundIn == null)
                {
                    throw new KeyException(idxVal.CodeForm(context.vm, 1));
                }
                return(result);
                //} else if (baseVal is ValList && idxVal is ValNumber) {
            }
            else if (baseValTypeInt == MiniscriptTypeInts.ValListTypeInt && idxVal is ValNumber)
            {
                return(((ValList)baseVal).GetElem(idxVal));
                //} else if (baseVal is ValString && idxVal is ValNumber) {
            }
            else if (baseValTypeInt == MiniscriptTypeInts.ValStringTypeInt && idxVal is ValNumber)
            {
                return(((ValString)baseVal).GetElem(idxVal));
            }
            else if (baseValTypeInt == MiniscriptTypeInts.ValCustomTypeInt)
            {
                ValCustom custom = baseVal as ValCustom;
                return(custom.Lookup(idxVal));
            }

            throw new TypeException("Type Exception: can't index into this type");
        }
Beispiel #14
0
        /// <summary>
        /// Look up a value in this dictionary, walking the __isa chain to find
        /// it in a parent object if necessary.
        /// </summary>
        /// <param name="key">key to search for</param>
        /// <returns>value associated with that key, or null if not found</returns>
        public Value Lookup(Value key)
        {
            Value  result = null;
            ValMap obj    = this;

            while (obj != null)
            {
                if (obj.map.TryGetValue(key, out result))
                {
                    return(result);
                }
                Value parent;
                if (!obj.map.TryGetValue(ValString.magicIsA, out parent))
                {
                    break;
                }
                obj = parent as ValMap;
            }
            return(null);
        }
Beispiel #15
0
        public void SetVar(string identifier, Value value)
        {
            if (identifier == "globals" || identifier == "locals")
            {
                throw new RuntimeException("can't assign to " + identifier);
            }

            if (variables == null)
            {
                variables = ValMap.Create();
            }
            var identifierStr = ValString.Create(identifier);

            if (variables.assignOverride == null || !variables.assignOverride(identifierStr, value))
            {
                //variables[identifier] = value;
                variables.SetElem(identifier, value, false);
                //variables.SetElem(identifier, value, true);
            }
            identifierStr.Unref();
        }
Beispiel #16
0
 public void Dispose()
 {
     Reset(true);
     code      = null;
     lineNum   = 0;
     variables = null;
     outerVars = null;
     if (args != null)
     {
         args.Clear();
     }
     parent                = null;
     resultStorage         = null;
     vm                    = null;
     partialResult         = default(Intrinsic.Result);
     implicitResultCounter = 0;
     if (_pool == null)
     {
         _pool = new Stack <Context>();
     }
     _pool.Push(this);
     //Console.WriteLine("ctx push");
 }
Beispiel #17
0
        public ValMap EvalCopy(TAC.Context context)
        {
            // Create a copy of this map, evaluating its members as we go.
            // This is used when a map 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 ValMap();

            foreach (Value k in map.Keys)
            {
                Value key   = k;                        // stupid C#!
                Value value = map[key];
                if (key is ValTemp || key is ValVar)
                {
                    key = key.Val(context);
                }
                if (value is ValTemp || value is ValVar)
                {
                    value = value.Val(context);
                }
                result.map[key] = value;
            }
            return(result);
        }
Beispiel #18
0
        public static ValMap Create()
        {
            //Console.WriteLine("Creating ValMap ID " + _num);
            if (_valuePool == null)
            {
                _valuePool = new ValuePool <ValMap>();
            }
            else
            {
                ValMap valMap = _valuePool.GetInstance();
                if (valMap != null)
                {
                    valMap._refCount = 1;
#if MINISCRIPT_DEBUG
                    valMap._id = _num++;
#endif
                    return(valMap);
                }
            }
#if MINISCRIPT_DEBUG
            _numInstancesAllocated++;
#endif
            return(new ValMap(true));
        }
Beispiel #19
0
 public override Value Val(Context context, out ValMap valueFoundIn)
 {
     valueFoundIn = null;
     return(context.GetVar(identifier));
 }
 public ValFunction BindAndCopy(ValMap contextVariables)
 {
     return(new ValFunction(function, contextVariables));
 }
 public ValFunction(Function function, ValMap outerVars)
 {
     this.function  = function;
     this.outerVars = outerVars;
 }
Beispiel #22
0
 /// <summary>
 /// This version of Val is like the one above, but also returns
 /// (via the output parameter) the ValMap the value was found in,
 /// which could be several steps up the __isa chain.
 /// </summary>
 /// <returns>The value.</returns>
 /// <param name="context">Context.</param>
 /// <param name="valueFoundIn">Value found in.</param>
 public virtual Value Val(TAC.Context context, out ValMap valueFoundIn)
 {
     valueFoundIn = null;
     return(this);
 }
Beispiel #23
0
        /// <summary>
        /// Look up the given identifier in the given sequence, walking the type chain
        /// until we either find it, or fail.
        /// </summary>
        /// <param name="sequence">Sequence (object) to look in.</param>
        /// <param name="identifier">Identifier to look for.</param>
        /// <param name="context">Context.</param>
        public static Value Resolve(Value sequence, string identifier, TAC.Context context, out ValMap valueFoundIn)
        {
            var includeMapType = true;

            valueFoundIn = null;
            int loopsLeft = 1000;                       // (max __isa chain depth)

            while (sequence != null)
            {
                if (sequence is ValTemp || sequence is ValVar)
                {
                    sequence = sequence.Val(context);
                }
                if (sequence is ValMap)
                {
                    // If the map contains this identifier, return its value.
                    Value result = null;
                    var   idVal  = TempValString.Get(identifier);
                    bool  found  = ((ValMap)sequence).map.TryGetValue(idVal, out result);
                    TempValString.Release(idVal);
                    if (found)
                    {
                        valueFoundIn = (ValMap)sequence;
                        return(result);
                    }

                    // Otherwise, if we have an __isa, try that next.
                    if (loopsLeft < 0)
                    {
                        return(null);                                           // (unless we've hit the loop limit)
                    }
                    if (!((ValMap)sequence).map.TryGetValue(ValString.magicIsA, out sequence))
                    {
                        // ...and if we don't have an __isa, try the generic map type if allowed
                        if (!includeMapType)
                        {
                            throw new KeyException(identifier);
                        }
                        sequence       = context.vm.mapType ?? Intrinsics.MapType();
                        includeMapType = false;
                    }
                }
                else if (sequence is ValList)
                {
                    sequence       = context.vm.listType ?? Intrinsics.ListType();
                    includeMapType = false;
                }
                else if (sequence is ValString)
                {
                    sequence       = context.vm.stringType ?? Intrinsics.StringType();
                    includeMapType = false;
                }
                else if (sequence is ValNumber)
                {
                    sequence       = context.vm.numberType ?? Intrinsics.NumberType();
                    includeMapType = false;
                }
                else if (sequence is ValFunction)
                {
                    sequence       = context.vm.functionType ?? Intrinsics.FunctionType();
                    includeMapType = false;
                }
                else
                {
                    throw new TypeException("Type Error (while attempting to look up " + identifier + ")");
                }
                loopsLeft--;
            }
            return(null);
        }
        /// <summary>
        /// Look up the given identifier in the given sequence, walking the type chain
        /// until we either find it, or fail.
        /// </summary>
        /// <param name="sequence">Sequence (object) to look in.</param>
        /// <param name="identifier">Identifier to look for.</param>
        /// <param name="context">Context.</param>
        public static Value Resolve(Value sequence, string identifier, Context context, out ValMap valueFoundIn)
        {
            var includeMapType = true;

            valueFoundIn = null;
            int loopsLeft = 1000;                       // (max __isa chain depth)

            while (sequence != null)
            {
                int seqTypeInt = sequence.GetBaseMiniscriptType();

                //if (sequence is ValTemp || sequence is ValVar)
                if (seqTypeInt == MiniscriptTypeInts.ValTempTypeInt || seqTypeInt == MiniscriptTypeInts.ValVarTypeInt)
                {
                    sequence   = sequence.Val(context, false);
                    seqTypeInt = sequence == null ? -1 : sequence.GetBaseMiniscriptType();
                }
                //if (sequence is ValMap) {
                if (seqTypeInt == MiniscriptTypeInts.ValMapTypeInt)
                {
                    // If the map contains this identifier, return its value.
                    ValMap seqMap = sequence as ValMap;
                    Value  result = null;
                    var    idVal  = ValString.Create(identifier);
                    bool   found  = seqMap.TryGetValue(idVal, out result);
                    idVal.Unref();
                    if (found)
                    {
                        valueFoundIn = seqMap;
                        return(result);
                    }

                    // Otherwise, if we have an __isa, try that next.
                    if (loopsLeft < 0)
                    {
                        return(null);        // (unless we've hit the loop limit)
                    }
                    if (!seqMap.TryGetValue(ValString.magicIsA, out sequence))
                    {
                        // ...and if we don't have an __isa, try the generic map type if allowed
                        if (!includeMapType)
                        {
                            throw new KeyException(identifier);
                        }
                        sequence       = context.vm.mapType ?? Intrinsics.MapType();
                        includeMapType = false;
                    }
                    //} else if (sequence is ValList) {
                }
                else if (seqTypeInt == MiniscriptTypeInts.ValListTypeInt)
                {
                    sequence       = context.vm.listType ?? Intrinsics.ListType();
                    includeMapType = false;
                    //} else if (sequence is ValString) {
                }
                else if (seqTypeInt == MiniscriptTypeInts.ValStringTypeInt)
                {
                    sequence       = context.vm.stringType ?? Intrinsics.StringType();
                    includeMapType = false;
                    //} else if (sequence is ValNumber) {
                }
                else if (seqTypeInt == MiniscriptTypeInts.ValNumberTypeInt)
                {
                    sequence       = context.vm.numberType ?? Intrinsics.NumberType();
                    includeMapType = false;
                    //} else if (sequence is ValFunction) {
                }
                else if (seqTypeInt == MiniscriptTypeInts.ValFunctionTypeInt)
                {
                    sequence       = context.vm.functionType ?? Intrinsics.FunctionType();
                    includeMapType = false;
                }
                else if (seqTypeInt == MiniscriptTypeInts.ValCustomTypeInt)
                {
                    ValCustom custom = sequence as ValCustom;
                    if (custom.Resolve(identifier, out Value result))
                    {
                        //valueFoundIn
                        return(result);
                    }
                    return(null);
                }
                else
                {
                    throw new TypeException("Type Error (while attempting to look up " + identifier + ")");
                }
                loopsLeft--;
            }
            return(null);
        }
Beispiel #25
0
            /// <summary>
            /// Evaluate this line and return the value that would be stored
            /// into the lhs.
            /// </summary>
            public Value Evaluate(Context context)
            {
                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)
                    {
                        return(rhsA.FullEval(context));
                    }
                    else if (rhsA == null)
                    {
                        return(null);
                    }
                    else
                    {
                        return(rhsA.Val(context));
                    }
                }
                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)
                    {
                        return(((ValList)rhsA).EvalCopy(context));
                    }
                    else if (rhsA is ValMap)
                    {
                        return(((ValMap)rhsA).EvalCopy(context));
                    }
                    else if (rhsA == null)
                    {
                        return(null);
                    }
                    else
                    {
                        return(rhsA.Val(context));
                    }
                }

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

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

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

                if (op == Op.ElemBofA && opB is ValString)
                {
                    // 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));
                }

                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));
                }

                if (opA is ValNumber)
                {
                    double fA = ((ValNumber)opA).value;
                    switch (op)
                    {
                    case Op.GotoA:
                        context.lineNum = (int)fA;
                        return(null);

                    case Op.GotoAifB:
                        if (opB != null && opB.BoolValue())
                        {
                            context.lineNum = (int)fA;
                        }
                        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)fA;
                        }
                        return(null);
                    }

                    case Op.GotoAifNotB:
                        if (opB == null || !opB.BoolValue())
                        {
                            context.lineNum = (int)fA;
                        }
                        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)fA, context, context.partialResult);
                        if (result.done)
                        {
                            context.partialResult = null;
                            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(new ValNumber(1.0 - AbsClamp01(fA)));
                    }
                    if (opB is ValNumber || opB == null)
                    {
                        double fB = opB != null ? ((ValNumber)opB).value : 0;
                        switch (op)
                        {
                        case Op.APlusB:
                            return(new ValNumber(fA + fB));

                        case Op.AMinusB:
                            return(new ValNumber(fA - fB));

                        case Op.ATimesB:
                            return(new ValNumber(fA * fB));

                        case Op.ADividedByB:
                            return(new ValNumber(fA / fB));

                        case Op.AModB:
                            return(new ValNumber(fA % fB));

                        case Op.APowB:
                            return(new ValNumber(Math.Pow(fA, fB)));

                        case Op.AEqualB:
                            return(ValNumber.Truth(fA == fB));

                        case Op.ANotEqualB:
                            return(ValNumber.Truth(fA != fB));

                        case Op.AGreaterThanB:
                            return(ValNumber.Truth(fA > fB));

                        case Op.AGreatOrEqualB:
                            return(ValNumber.Truth(fA >= fB));

                        case Op.ALessThanB:
                            return(ValNumber.Truth(fA < fB));

                        case Op.ALessOrEqualB:
                            return(ValNumber.Truth(fA <= fB));

                        case Op.AAndB:
                            if (!(opB is ValNumber))
                            {
                                fB = opB != null && opB.BoolValue() ? 1 : 0;
                            }
                            return(new ValNumber(Clamp01(fA * fB)));

                        case Op.AOrB:
                            if (!(opB is ValNumber))
                            {
                                fB = opB != null && opB.BoolValue() ? 1 : 0;
                            }
                            return(new ValNumber(Clamp01(fA + fB - fA * fB)));

                        default:
                            break;
                        }
                    }
                    else if (opB is ValString)
                    {
                        // number (op) string
                        string sA = opA.ToString();
                        string sB = opB.ToString();
                        switch (op)
                        {
                        case Op.APlusB:
                            return(new ValString(sA + sB));

                        default:
                            break;
                        }
                    }
                }
                else if (opA is ValString)
                {
                    string sA = ((ValString)opA).value;
                    switch (op)
                    {
                    case Op.APlusB:
                    {
                        if (opB == null)
                        {
                            return(opA);
                        }
                        String sB = opB.ToString(context.vm);
                        if (sA.Length + sB.Length > ValString.maxSize)
                        {
                            throw new LimitExceededException("string too large");
                        }
                        return(new ValString(sA + sB));
                    }

                    case Op.AMinusB:
                    {
                        if (opB == null)
                        {
                            return(opA);
                        }
                        string sB = opB.ToString(context.vm);
                        if (sA.EndsWith(sB))
                        {
                            sA = sA.Substring(0, sA.Length - sB.Length);
                        }
                        return(new ValString(sA));
                    }

                    case Op.ATimesB:
                    case 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 * sA.Length > ValString.maxSize)
                        {
                            throw new LimitExceededException("string too large");
                        }
                        var result = new System.Text.StringBuilder();
                        for (int i = 0; i < repeats; i++)
                        {
                            result.Append(sA);
                        }
                        int extraChars = (int)(sA.Length * (factor - repeats));
                        if (extraChars > 0)
                        {
                            result.Append(sA.Substring(0, extraChars));
                        }
                        return(new ValString(result.ToString()));
                    }

                    case Op.AEqualB:
                        return(ValNumber.Truth(string.Compare(sA, opB.ToString(context.vm)) == 0));

                    case Op.ANotEqualB:
                        return(ValNumber.Truth(string.Compare(sA, opB.ToString(context.vm)) != 0));

                    case Op.AGreaterThanB:
                        return(ValNumber.Truth(string.Compare(sA, opB.ToString(context.vm)) > 0));

                    case Op.AGreatOrEqualB:
                        return(ValNumber.Truth(string.Compare(sA, opB.ToString(context.vm)) >= 0));

                    case Op.ALessThanB:
                        return(ValNumber.Truth(string.Compare(sA, opB.ToString(context.vm)) < 0));

                    case Op.ALessOrEqualB:
                        return(ValNumber.Truth(string.Compare(sA, opB.ToString(context.vm)) <= 0));

                    case Op.ElemBofA:
                    case Op.ElemBofIterA:
                    {
                        int idx = opB.IntValue();
                        Check.Range(idx, -sA.Length, sA.Length - 1, "string index");
                        if (idx < 0)
                        {
                            idx += sA.Length;
                        }
                        return(new ValString(sA.Substring(idx, 1)));
                    }

                    case Op.LengthOfA:
                        return(new ValNumber(sA.Length));

                    default:
                        break;
                    }
                }
                else if (opA is ValList)
                {
                    List <Value> list = ((ValList)opA).values;
                    if (op == Op.ElemBofA || op == Op.ElemBofIterA)
                    {
                        // list indexing
                        int idx = opB.IntValue();
                        Check.Range(idx, -list.Count, list.Count - 1, "list index");
                        if (idx < 0)
                        {
                            idx += list.Count;
                        }
                        return(list[idx]);
                    }
                    else if (op == Op.LengthOfA)
                    {
                        return(new ValNumber(list.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");
                        List <Value> list2 = ((ValList)opB).values;
                        if (list.Count + list2.Count > ValList.maxSize)
                        {
                            throw new LimitExceededException("list too large");
                        }
                        List <Value> result = new List <Value>(list.Count + list2.Count);
                        foreach (Value v in list)
                        {
                            result.Add(v == null ? null : v.Val(context));
                        }
                        foreach (Value v in list2)
                        {
                            result.Add(v == null ? null : v.Val(context));
                        }
                        return(new ValList(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(new ValList());
                        }
                        int finalCount = (int)(list.Count * factor);
                        if (finalCount > ValList.maxSize)
                        {
                            throw new LimitExceededException("list too large");
                        }
                        List <Value> result = new List <Value>(finalCount);
                        for (int i = 0; i < finalCount; i++)
                        {
                            result.Add(list[i % list.Count]);
                        }
                        return(new ValList(result));
                    }
                    else if (op == Op.NotA)
                    {
                        return(ValNumber.Truth(!opA.BoolValue()));
                    }
                }
                else if (opA is ValMap)
                {
                    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 = new ValSeqElem(opA, opB);
                        return(se.Val(context));
                        // (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(new ValNumber(((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
                        Dictionary <Value, Value> map = ((ValMap)opA).map;
                        Check.Type(opB, typeof(ValMap), "map combination");
                        Dictionary <Value, Value> map2 = ((ValMap)opB).map;
                        ValMap result = new ValMap();
                        foreach (KeyValuePair <Value, Value> kv in map)
                        {
                            result.map[kv.Key] = kv.Value.Val(context);
                        }
                        foreach (KeyValuePair <Value, Value> kv in map2)
                        {
                            result.map[kv.Key] = kv.Value.Val(context);
                        }
                        return(result);
                    }
                    else if (op == Op.NotA)
                    {
                        return(ValNumber.Truth(!opA.BoolValue()));
                    }
                }
                else if (opA is ValFunction && opB is ValFunction)
                {
                    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
                {
                    // opA is something else... perhaps null
                    switch (op)
                    {
                    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 fA = opA != null && opA.BoolValue() ? 1 : 0;
                    double fB;
                    if (opB is ValNumber)
                    {
                        fB = ((ValNumber)opB).value;
                    }
                    else
                    {
                        fB = opB != null && opB.BoolValue() ? 1 : 0;
                    }
                    double result;
                    if (op == Op.AAndB)
                    {
                        result = fA * fB;
                    }
                    else
                    {
                        result = 1.0 - (1.0 - AbsClamp01(fA)) * (1.0 - AbsClamp01(fB));
                    }
                    return(new ValNumber(result));
                }
                return(null);
            }
 public override Value Val(Context context, out ValMap valueFoundIn)
 {
     //Console.WriteLine("valref 2");
     Ref();
     return(base.Val(context, out valueFoundIn));
 }
Beispiel #27
0
        /// <summary>
        /// Get the value of a variable available in this context (including
        /// locals, globals, and intrinsics).  Raise an exception if no such
        /// identifier can be found.
        /// </summary>
        /// <param name="identifier">name of identifier to look up</param>
        /// <returns>value of that identifier</returns>
        public Value GetVar(string identifier)
        {
            // check for special built-in identifiers 'locals' and 'globals'
            if (identifier == "locals")
            {
                if (variables == null)
                {
                    variables = ValMap.Create();
                }
                return(variables);
            }
            if (identifier == "globals")
            {
                if (root.variables == null)
                {
                    root.variables = ValMap.Create();
                }
                return(root.variables);
            }
            if (identifier == "outer")
            {
                // return module variables, if we have them; else globals
                if (outerVars != null)
                {
                    return(outerVars);
                }
                if (root.variables == null)
                {
                    root.variables = ValMap.Create();
                }
                return(root.variables);
            }

            // check for a local variable
            Value result;

            if (variables != null && variables.TryGetValue(identifier, out result))
            {
                return(result);
            }

            // check for a module variable
            if (outerVars != null && outerVars.TryGetValue(identifier, out result))
            {
                return(result);
            }

            // OK, we don't have a local or module variable with that name.
            // Check the global scope (if that's not us already).
            if (parent != null)
            {
                Context globals = root;
                if (globals.variables != null && globals.variables.ContainsKey(identifier))
                {
                    return(globals.variables[identifier]);
                }
            }

            // Finally, check intrinsics.
            Intrinsic intrinsic = Intrinsic.GetByName(identifier);

            if (intrinsic != null)
            {
                return(intrinsic.GetFunc());
            }

            // No luck there either?  Undefined identifier.
            throw new UndefinedIdentifierException(identifier);
        }
Beispiel #28
0
 public override Value Val(Context context, out ValMap valueFoundIn)
 {
     valueFoundIn = null;
     return(context.GetTemp(tempNum));
 }
Beispiel #29
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);
        }
Beispiel #30
0
 public override Value Val(TAC.Context context, out ValMap valueFoundIn)
 {
     valueFoundIn = null;
     return(null);
 }