public void SetElem(string index, Value value, bool takeValueRef) { ValString keyStr = ValString.Create(index); SetElem(keyStr, value, takeValueRef); keyStr.Unref(); }
/// <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); }
/// <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 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 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); }
/// <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); }
public static ValString Str(string value) { return(ValString.Create(value)); }
/// <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); }