public void SetElem(string index, Value value, bool takeValueRef) { ValString keyStr = ValString.Create(index); SetElem(keyStr, value, takeValueRef); keyStr.Unref(); }
public void Insert(int idx, Value value) { ValString str = value as ValString; value?.Ref(); values.Insert(StartIndex + idx, value); }
/// <summary> /// Look up the given identifier as quickly as possible, without /// walking the __isa chain or doing anything fancy. (This is used /// when looking up local variables.) /// </summary> /// <param name="identifier">identifier to look up</param> /// <returns>true if found, false if not</returns> public bool TryGetValue(string identifier, out Value value) { var idVal = ValString.Create(identifier); bool result = TryGetValue(idVal, out value); idVal.Unref(); return(result); }
public void Add(Value value, bool takeRef = true) { ValString str = value as ValString; if (takeRef) { value?.Ref(); } values.Add(value); }
public Value GetLocal(ValString identifier, Value defaultValue = null) { Value result; if (variables != null && variables.TryGetValue(identifier, out result)) { return(result); } return(defaultValue); }
/// <summary> /// Convenience method to check whether the map contains a given string key. /// </summary> /// <param name="identifier">string key to check for</param> /// <returns>true if the map contains that key; false otherwise</returns> public bool ContainsKey(string identifier) { if (TryGetInternalBuiltIn(identifier, out Value existing)) { return(existing != null); } var idVal = ValString.Create(identifier); bool result = map.ContainsKey(idVal); idVal.Unref(); return(result); }
/// <summary> /// Accessor to get/set on element of this map by a string key, walking /// the __isa chain as needed. (Note that if you want to avoid that, then /// simply look up your value in .map directly.) /// </summary> /// <param name="identifier">string key to get/set</param> /// <returns>value associated with that key</returns> public Value this [string identifier] { //TODO I think we might unecessarily be walking up the _isa chain here get { var idVal = ValString.Create(identifier); Value result = Lookup(idVal); idVal.Unref(); return(result); } set { SetElem(identifier, value, true); } }
public Value GetElem(Value index) { var i = index.IntValue(); if (i < 0) { i += value.Length; } if (i < 0 || i >= value.Length) { throw new IndexException("Index Error (string index " + index + " out of range)"); } return(ValString.Create(value.Substring(i, 1))); }
public Value GetVar(Value identifier) { // 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. ValString identStrVal = identifier as ValString; string identStr = identStrVal == null ? string.Empty : identStrVal.value; if (!string.IsNullOrEmpty(identStr)) { Intrinsic intrinsic = Intrinsic.GetByName(identStr); if (intrinsic != null) { return(intrinsic.GetFunc()); } } // No luck there either? Undefined identifier. throw new UndefinedIdentifierException(identStr); }
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"); }
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(); }
public bool Remove(Value keyVal) { // Check against common entries first, for perf ValString indexStr = keyVal as ValString; Value existing; if (indexStr != null) { if (TrySetInternalBuiltIn(indexStr, null, out existing)) { // We return true only if we had an existing value if (existing != null) { _isCountDirty = true; _isKeysDirty = true; _isValuesDirty = true; return(true); } return(false); } } // Pull the current key/value so that we can unref it if (map.TryGetValue(keyVal, out existing)) { existing.Unref(); // Try to get the key that's there and unref it Value existingKey = RemoveBySwap(_mapKeys, keyVal); if (existingKey != null) { existingKey.Unref(); } map.Remove(keyVal); _isCountDirty = true; _isKeysDirty = true; _isValuesDirty = true; return(true); } return(false); }
public override bool Resolve(string identifier, out Value ret) { switch (identifier) { case NumAValueName: ret = ValNumber.Create(NumA); return(true); case StringBValueName: ret = ValString.Create(StrB); return(true); case InverseValueName: ret = Inverse(); return(true); case IsPotatoFunction: ret = _isPotatoFunction.GetFunc(); return(true); } ret = null; return(false); }
public static void InitializeIntrinsics() { if (_hasStaticInit) { return; } _hasStaticInit = true; // Load the constructor Intrinsic ctor = Intrinsic.Create("ExampleCustom"); ctor.AddParam(NumAValueName, 0.0); ctor.AddParam(StringBValueName, ""); ctor.code = (context, partialResult) => { ValNumber numA = context.GetVar(NumAValueName) as ValNumber; ValString strB = context.GetVar(StringBValueName) as ValString; ExampleCustomVal customVal = new ExampleCustomVal( numA != null ? (float)numA.value : 0, strB != null ? strB.value : ""); return(new Intrinsic.Result(customVal)); }; _isPotatoFunction = Intrinsic.Create(IsPotatoFunction, false); _isPotatoFunction.AddParam("item"); _isPotatoFunction.code = (context, partialResult) => { ValString str = context.GetVar("item") as ValString; if (str != null && str.value == "potato") { return(Intrinsic.Result.True); } return(Intrinsic.Result.False); }; }
public void SetElem(Value index, Value value, bool takeValueRef) { var i = index.IntValue(); if (i < 0) { i += Count; } if (i < 0 || i >= Count) { throw new IndexException("Index Error (list index " + index + " out of range)"); } i += StartIndex; ValString str = value as ValString; // Unref existing values[i]?.Unref(); // Ref new if (takeValueRef) { value?.Ref(); } values[i] = value; }
private bool TryGetInternalBuiltIn(Value identifier, out Value value) { ValString str = identifier as ValString; if (str == null) { value = null; return(false); } // If the ValString wasn't made by ValString as a built-in, we // can exit early if (!str.IsBuiltIn) { value = null; return(false); } // Internally, unity turns string switch case into a dictionary // which is fast, but working with the hashes can be sorta slow // so we just use the instance ID on the val string, as int // comparison is very fast //TODO this could be a static BST or a hash map! int recvID = str.InstanceID; if (recvID == ValString.selfStr.InstanceID) { value = selfVal; return(true); } if (recvID == ValString.magicIsA.InstanceID) { value = isaVal; return(true); } if (recvID == ValString.eventsStr.InstanceID) { value = eventsVal; return(true); } if (recvID == ValString.eventValsStr.InstanceID) { value = eventValsVal; return(true); } if (recvID == ValString.isAtEndStr.InstanceID) { value = isAtEndVal; return(true); } if (recvID == ValString.xStr.InstanceID) { value = xVal; return(true); } if (recvID == ValString.yStr.InstanceID) { value = yVal; return(true); } if (recvID == ValString.zStr.InstanceID) { value = zVal; return(true); } if (recvID == ValString.wStr.InstanceID) { value = wVal; return(true); } if (recvID == ValString.nameStr.InstanceID) { value = nameVal; return(true); } if (recvID == ValString.positionStr.InstanceID) { value = positionVal; return(true); } if (recvID == ValString.rotationStr.InstanceID) { value = rotationVal; return(true); } if (recvID == ValString.velocityStr.InstanceID) { value = velocityVal; return(true); } if (recvID == ValString.angularVelocityStr.InstanceID) { value = angularVelocityVal; return(true); } if (recvID == ValString.forwardStr.InstanceID) { value = forwardVal; return(true); } if (recvID == ValString.rightStr.InstanceID) { value = rightVal; return(true); } if (recvID == ValString.timeStr.InstanceID) { value = timeVal; return(true); } if (recvID == ValString.deltaTimeStr.InstanceID) { value = deltaTimeVal; return(true); } if (recvID == ValString.frameCountStr.InstanceID) { value = frameCountVal; return(true); } value = null; return(false); }
private bool TrySetInternalBuiltIn(Value identifier, Value value, out Value previousVal) { ValString str = identifier as ValString; if (str == null) { previousVal = null; return(false); } // If the ValString wasn't made by ValString as a built-in, we // can exit early if (!str.IsBuiltIn) { previousVal = null; return(false); } int recvID = str.InstanceID; if (recvID == ValString.selfStr.InstanceID) { previousVal = selfVal; selfVal?.Unref(); selfVal = value; return(true); } if (recvID == ValString.magicIsA.InstanceID) { previousVal = isaVal; isaVal?.Unref(); isaVal = value; return(true); } if (recvID == ValString.eventsStr.InstanceID) { previousVal = eventsVal; eventsVal?.Unref(); eventsVal = value; return(true); } if (recvID == ValString.eventValsStr.InstanceID) { previousVal = eventValsVal; eventValsVal?.Unref(); eventValsVal = value; return(true); } if (recvID == ValString.isAtEndStr.InstanceID) { previousVal = isAtEndVal; isAtEndVal?.Unref(); isAtEndVal = value; return(true); } if (recvID == ValString.xStr.InstanceID) { previousVal = xVal; xVal?.Unref(); xVal = value; return(true); } if (recvID == ValString.yStr.InstanceID) { previousVal = yVal; yVal?.Unref(); yVal = value; return(true); } if (recvID == ValString.zStr.InstanceID) { previousVal = zVal; zVal?.Unref(); zVal = value; return(true); } if (recvID == ValString.wStr.InstanceID) { previousVal = wVal; wVal?.Unref(); wVal = value; return(true); } if (recvID == ValString.nameStr.InstanceID) { previousVal = nameVal; nameVal?.Unref(); nameVal = value; return(true); } if (recvID == ValString.positionStr.InstanceID) { previousVal = positionVal; positionVal?.Unref(); positionVal = value; return(true); } if (recvID == ValString.rotationStr.InstanceID) { previousVal = rotationVal; rotationVal?.Unref(); rotationVal = value; return(true); } if (recvID == ValString.velocityStr.InstanceID) { previousVal = velocityVal; velocityVal?.Unref(); velocityVal = value; return(true); } if (recvID == ValString.angularVelocityStr.InstanceID) { previousVal = angularVelocityVal; angularVelocityVal?.Unref(); angularVelocityVal = value; return(true); } if (recvID == ValString.forwardStr.InstanceID) { previousVal = forwardVal; forwardVal?.Unref(); forwardVal = value; return(true); } if (recvID == ValString.rightStr.InstanceID) { previousVal = rightVal; rightVal?.Unref(); rightVal = value; return(true); } if (recvID == ValString.timeStr.InstanceID) { previousVal = timeVal; timeVal?.Unref(); timeVal = value; return(true); } if (recvID == ValString.deltaTimeStr.InstanceID) { previousVal = deltaTimeVal; deltaTimeVal?.Unref(); deltaTimeVal = value; return(true); } if (recvID == ValString.frameCountStr.InstanceID) { previousVal = frameCountVal; frameCountVal?.Unref(); frameCountVal = value; return(true); } previousVal = null; return(false); }
public static ValString Str(string value) { return(ValString.Create(value)); }
public void SetElem(Value index, Value value, bool takeValueRef, bool takeIndexRef = true) { ValNumber newNum = value as ValNumber; //Console.WriteLine("Map set elem " + index.ToString() + ": " + value.ToString()); if (takeValueRef) { value?.Ref(); } if (takeIndexRef) { index?.Ref(); } if (index == null) { index = ValNull.instance; } if (assignOverride == null || !assignOverride(index, value)) { // Check against common entries first, for perf ValString indexStr = index as ValString; if (indexStr != null) { // We want to replicate the behavior of a map, so to // preserve the way that you can set a key to null, we // simply store a ValNull here, and pull out a ValNull // later but just return a null Value builtInVal = value ?? ValNull.instance; if (TrySetInternalBuiltIn(indexStr, builtInVal, out Value oldVal)) { // If we're overwriting a value, keep count/keys if (oldVal != null) { _isValuesDirty = true; } else { _isCountDirty = true; _isKeysDirty = true; _isValuesDirty = true; } return; } } if (map.TryGetValue(index, out Value existing)) { // Unref the value that's currently there if (existing != null) { existing.Unref(); // There can be null entries in this list } // Try to get the key that's there and unref it Value existingKey = RemoveBySwap(_mapKeys, index); map.Remove(existingKey); if (existingKey != null) { existingKey.Unref(); } // Overwrote value, count didn't change but keys/values did _isKeysDirty = true; _isValuesDirty = true; } else { _isCountDirty = true; _isKeysDirty = true; _isValuesDirty = true; } _mapKeys.Add(index); map[index] = value; } }
//TODO add create with ValString for fast add public static ValString Create(string val, bool usePool = true) { if (!usePool) { return(new ValString(val, false)); } switch (val) { case "s": return(sStr); case "self": return(selfStr); case "to": return(toStr); case "from": return(fromStr); case "__isa": return(magicIsA); case "seq": return(seqStr); case "super": return(superStr); case "len": return(lenStr); case "__events": return(eventsStr); case "__eventVals": return(eventValsStr); case "__isAtEnd": return(isAtEndStr); case "yield": return(yieldStr); case " ": return(spaceStr); case "x": return(xStr); case "y": return(yStr); case "z": return(zStr); case "w": return(wStr); case "name": return(nameStr); case "position": return(positionStr); case "rotation": return(rotationStr); case "velocity": return(velocityStr); case "angularVelocity": return(angularVelocityStr); case "forward": return(forwardStr); case "right": return(rightStr); case "time": return(timeStr); case "deltaTime": return(deltaTimeStr); case "frameCount": return(frameCountStr); } //Console.WriteLine("Alloc str \"" + val + "\" num " + _num); if (_valuePool == null) { _valuePool = new ValuePool <ValString>(); } else { ValString valStr = _valuePool.GetInstance(); if (valStr != null) { valStr._refCount = 1; valStr.value = val; #if MINISCRIPT_DEBUG valStr._id = _num++; #endif return(valStr); } } #if MINISCRIPT_DEBUG _numInstancesAllocated++; #endif return(new ValString(val, true)); }
/// <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); }
/// <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); }