/// <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; } }
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); }
/// <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); }
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"); }
/// <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(); } }
public override Value Val(TAC.Context context, out ValMap valueFoundIn) { valueFoundIn = null; if (this == self) { return(context.self); } return(context.GetVar(identifier)); }
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); } }
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; }
/// <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); }
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); }
/// <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); }
/// <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"); }
/// <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); }
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 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"); }
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); }
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)); }
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; }
/// <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); }
/// <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); }
/// <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)); }
/// <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); }
public override Value Val(Context context, out ValMap valueFoundIn) { valueFoundIn = null; return(context.GetTemp(tempNum)); }
/// <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 override Value Val(TAC.Context context, out ValMap valueFoundIn) { valueFoundIn = null; return(null); }