/// <summary> /// If bCreate, always set value on current scope, /// else, change value on scope value exists on or throw exception if it doesn't exist /// </summary> /// <returns>true if it actually set a value</returns> internal static bool SetOnScope(IScope scope, string key, Value value, bool bCreate, bool bOverload, bool bInitOnly) { // figure out the scope to modify IScope where; if (bCreate) { where = scope; } else { where = scope.Exists(key); if (where == null) throw new Loki3Exception().AddBadToken(new Token(key)); } // if we're setting a function, it may be an overload if (bOverload && value.Type == ValueType.Function) { // first, if there's nothing there, see if key exists on an ancestor scope bool bFound = where.AsMap.ContainsKey(key); if (!bFound) { IScope ancestor = scope.Exists(key); if (ancestor != null) { Value existing = ancestor.AsMap[key]; if (existing.Type == ValueType.Function) { // copy function(s) to this scope so we can overload it // and yet this definition only exists in this scope Value copiedToThisScope = existing.Copy(); where.SetValue(key, copiedToThisScope); bFound = true; } } } if (bFound) { Value existing = where.AsMap[key]; ValueFunctionOverload overload = null; if (existing.Type == ValueType.Function) { // change function value into an overload value overload = existing as ValueFunctionOverload; if (overload == null) { overload = new ValueFunctionOverload(existing as ValueFunction); overload.Add(value as ValueFunction); where.SetValue(key, overload); } else { overload.Add(value as ValueFunction); } return true; } } } if (bInitOnly && where.AsMap.ContainsKey(key)) return false; where.SetValue(key, value); return true; }
public void TestBasic() { ValueFunctionOverload overload = new ValueFunctionOverload(); IScope scope = new ScopeChain(); { // add a single function & make sure it's called at right time & fails at right time ValueFunction numInt = new Add(ValueType.Number, ValueType.Int, 0); overload.Add(numInt); Value a = overload.Eval(null, MakePair(3, 4, true, true), scope, scope, null, null); Assert.AreEqual(7, a.AsInt); Value b = overload.Eval(null, MakePair(3, 5, false, true), scope, scope, null, null); Assert.AreEqual(8, b.AsFloat); bool bThrew = false; try { overload.Eval(null, MakePair(3, 4, false, false), scope, scope, null, null); } catch (Loki3Exception) { bThrew = true; } Assert.IsTrue(bThrew); } { // add a second function & make sure both succeed & fail at right time // this one is more specific so should get called first when signature matches ValueFunction intInt = new Add(ValueType.Int, ValueType.Int, 1); overload.Add(intInt); Value a = overload.Eval(null, MakePair(3, 4, true, true), scope, scope, null, null); Assert.AreEqual(8, a.AsInt); // calls 2nd version Value b = overload.Eval(null, MakePair(3, 5, false, true), scope, scope, null, null); Assert.AreEqual(8, b.AsFloat); // calls 1st version bool bThrew = false; try { // still no match for this one overload.Eval(null, MakePair(3, 4, false, false), scope, scope, null, null); } catch (Loki3Exception) { bThrew = true; } Assert.IsTrue(bThrew); } { // try adding a postfix function to the overload bool bThrew = false; try { ValueFunction post = new Post(); overload.Add(post); } catch (Loki3Exception e) { Assert.AreEqual("prefix", e.Errors[Loki3Exception.keyExpectedFix].ToString()); Assert.AreEqual("postfix", e.Errors[Loki3Exception.keyActualFix].ToString()); bThrew = true; } Assert.IsTrue(bThrew); } }