public void Test() { try { ScopeChain scope = new ScopeChain(); AllBuiltins.RegisterAll(scope); EvalFile.Do("l3/bootstrap.l3", scope); EvalFile.Do("l3/unittest.l3", scope); // use the loki3 unittest framework to test the code { Value v = TestSupport.ToValue("unittest [ :l3/help.l3 :l3/help_tests.l3 ]", scope); Assert.True(v.AsBool); } { Value v = TestSupport.ToValue("unittest [ :l3/test.l3 :l3/test_tests.l3 ]", scope); Assert.True(v.AsBool); } { Value v = TestSupport.ToValue("runTestFile :l3/pattern_tests.l3", scope); Assert.True(v.AsBool); } } catch (Loki3Exception e) { Assert.Fail(e.ToString()); } }
static ScopeChain Bootstrap() { ScopeChain scope = new ScopeChain(); AllBuiltins.RegisterAll(scope); EvalFile.Do("bootstrap.l3", scope); EvalFile.Do("help.l3", scope); return scope; }
static IScope CreateScope() { ScopeChain scope = new ScopeChain(); Conditional.Register(scope); scope.SetValue("[", new ValueDelimiter("]", DelimiterType.AsArray)); scope.SetValue("'", new ValueDelimiter("'", DelimiterType.AsString)); return scope; }
static IScope CreateValueScope() { ScopeChain scope = new ScopeChain(); Logic.Register(scope); scope.SetValue("[", new ValueDelimiter("]", DelimiterType.AsArray)); scope.SetValue("`", new ValueDelimiter("`", DelimiterType.AsRaw)); return scope; }
static IScope CreateModuleScope() { ScopeChain scope = new ScopeChain(); Module.Register(scope); Values.Register(scope); scope.SetValue("'", new ValueDelimiter("'", DelimiterType.AsString)); scope.SetValue("{", new ValueDelimiter("}", DelimiterType.AsArray, new CreateMap())); return scope; }
public void TestComplex() { try { IScope scope = new ScopeChain(GetBootstrapScope()); // define complex math string[] lines = { // complex [ 1 2 ] -> 1+2i ":complex v= func1 [ ->x ->y ]", " { :real x :imaginary y }", ":complex @order 1", // 5 i -> 5i ":i v= .( ->y 1func", " { :real 0 :imaginary y }", ":i @order 1", // addition // todo: adjust when we have function overloading & default values ":+c v= .( { :real ->x1 :imaginary ->y1 } 1func1 { :real ->x2 :imaginary ->y2 }", " { :real ( x1 + x2 ) :imaginary ( y1 + y2 ) }", ":+i v= .( ->x1 1func1 { :real ->x2 :imaginary ->y2 }", " { :real ( x1 + x2 ) :imaginary y2 }", }; LineConsumer requestor = new LineConsumer(lines); EvalLines.Do(requestor, scope); { Value value = TestSupport.ToValue("complex [ 1 2 ]", scope); Assert.AreEqual("{ :real 1 , :imaginary 2 }", value.ToString()); } { Value value = TestSupport.ToValue("4 i", scope); Assert.AreEqual("{ :real 0 , :imaginary 4 }", value.ToString()); } { Value value = TestSupport.ToValue("1 +i 3 i", scope); Assert.AreEqual("{ :real 1 , :imaginary 3 }", value.ToString()); } { Value value = TestSupport.ToValue("( 1 +i 3 i ) +c complex [ 4 6 ]", scope); Assert.AreEqual("{ :real 5 , :imaginary 9 }", value.ToString()); } } catch (Loki3Exception e) { Assert.Fail(e.ToString()); } }
static IScope CreateStringScope() { ScopeChain scope = new ScopeChain(); String.Register(scope); ValueDelimiter square = new ValueDelimiter("]", DelimiterType.AsArray); scope.SetValue("[", square); ValueDelimiter str = new ValueDelimiter("'", DelimiterType.AsString); scope.SetValue("'", str); ValueDelimiter curly = new ValueDelimiter("}", DelimiterType.AsArray, new CreateMap()); scope.SetValue("{", curly); return scope; }
public void TestBindFunction() { IScope scope = CreateValueScope(); // a scope with a value for a IScope scopeA = new ScopeChain(scope); scopeA.SetValue("a", new ValueInt(3)); // create a new function that returns a & is bound to a map where a=42 TestSupport.ToValue("l3.setValue { :key :testBound :value ( l3.bindFunction { :function ( l3.createFunction { :post :x :body [ ' a ' ] } ) :map { :a 42 } } ) }", scope); // then eval it against a scope where a!=42 Value value = TestSupport.ToValue("testBound 1", scopeA); Assert.AreEqual(42, value.AsInt); value = TestSupport.ToValue("a", scopeA); Assert.AreEqual(3, value.AsInt); }
internal override Value Eval(Value arg, IScope scope) { Map map = arg.AsMap; List<DelimiterNode> check = (map["check"] as ValueRaw).GetValue().Nodes; Value changeVal = map["change"]; List<DelimiterNode> change = changeVal is ValueNil ? null : (changeVal as ValueRaw).GetValue().Nodes; bool checkFirst = map["checkFirst?"].AsBool; // todo: consolidate these bodies, one from an explicit param & one from the following lines Value valueBody = map.ContainsKey("body") ? map["body"] : map[ValueFunction.keyBody]; bool isFirst = true; Value result = ValueBool.False; while (true) { // check if we should stop loop if (!isFirst || checkFirst) { Value retval = EvalList.Do(check, scope); if (!retval.AsBool) return result; // last value of body } else { isFirst = false; } // use bound scope if available IScope parentScope = scope; if (valueBody is ValueRaw) { IScope rawScope = (valueBody as ValueRaw).Scope; if (rawScope != null) parentScope = rawScope; } // eval body IScope local = new ScopeChain(parentScope); result = EvalBody.Do(valueBody, local); local.Exit(); // if per-loop code was passed, run it if (change != null) EvalList.Do(change, scope); } }
internal override Value Eval(Value arg, IScope scope) { Map map = arg.AsMap; ValueFunction function = map["function"] as ValueFunction; ValueMap mapOrScope = map["map"] as ValueMap; Value prevValue = map["previous"]; Value nextValue = map["next"]; // either use the scope the map represents // or dummy up a scope for the passed in map IScope where = mapOrScope.Scope; if (where == null) where = new ScopeChain(mapOrScope.AsMap); DelimiterNodeValue prev = (prevValue == ValueNil.Nil ? null : new DelimiterNodeValue(prevValue)); DelimiterNodeValue next = (nextValue == ValueNil.Nil ? null : new DelimiterNodeValue(nextValue)); Value value = function.Eval(prev, next, where, where, null, null); where.Exit(); return value; }
public void Test() { try { IScope scope = new ScopeChain(GetBootstrapScope()); { // a function that needs params but doesn't get them is just a function Value value = TestSupport.ToValue("=enum", scope); Assert.AreEqual(ValueType.Function, value.Type); } { // test eval order Value value = TestSupport.ToValue("2 * 2 + 3", scope); Assert.AreEqual(7, value.AsInt); value = TestSupport.ToValue("2 + 2 * 3", scope); Assert.AreEqual(8, value.AsInt); } } catch (Loki3Exception e) { Assert.Fail(e.ToString()); } }
public void RunUnittests() { try { IScope scope = new ScopeChain(GetBootstrapScope()); EvalFile.Do("l3/unittest.l3", scope); EvalFile.Do("l3/help.l3", scope); // make sure all functions have @doc Value a = TestSupport.ToValue("checkDocs currentScope", scope); if (!a.AsArray[0].AsBool) // make it obvious which functions need @doc Assert.AreEqual("[ ]", a.AsArray[2].AsArray); // currently this runs checkDocs as well Value v = TestSupport.ToValue("unittest [ :l3/bootstrap.l3 :l3/bootstrap_tests.l3 ]", scope); // if this fails, there may be output that describes which unittest & which assert Assert.True(v.AsBool); } catch (Loki3Exception e) { Assert.Fail(e.ToString()); } }
internal override Value Eval(Value arg, IScope scope) { Map map = arg.AsMap; Value value = map["value"]; IScope where = scope; Value valueMap = map["map"]; if (!valueMap.IsNil) { // either use the scope the map represents // or dummy up a scope for the passed in map ValueMap mapOrScope = valueMap as ValueMap; where = mapOrScope.Scope; if (where == null) where = new ScopeChain(mapOrScope.AsMap); } Value retval = Utility.EvalValue(value, where); where.Exit(); return retval; }
internal override Value Eval(Value arg, IScope scope) { Map map = arg.AsMap; Value obj = map["map"]; string prompt = map["prompt"].AsString; bool registerBuiltins = map["registerBuiltins?"].AsBool; IScope scopeToUse = scope; if (obj != ValueNil.Nil) { ValueMap vMap = obj as ValueMap; if (vMap.Scope != null) scopeToUse = vMap.Scope; else scopeToUse = new ScopeChain(vMap.AsMap); } if (registerBuiltins) AllBuiltins.RegisterAll(scopeToUse); if (map["fileToLoad"].Type == ValueType.String) { string file = map["fileToLoad"].AsString; EvalFile.Do(file, scopeToUse); } loki3.core.Repl.Do(scopeToUse, prompt); scopeToUse.Exit(); return ValueNil.Nil; }
internal override Value Eval(Value arg, IScope scope) { Map map = arg.AsMap; Value collection = map["collection"]; // todo: consolidate these bodies, one from an explicit param & one from the following lines Value valueBody = map.ContainsKey("body") ? map["body"] : map[ValueFunction.keyBody]; IScope bodyScope = scope; if (valueBody is ValueRaw) { IScope tempScope = (valueBody as ValueRaw).Scope; if (tempScope != null) bodyScope = tempScope; } // todo: abstract iteration to avoid these ifs Value result = ValueNil.Nil; if (collection is ValueString) { string s = collection.AsString; foreach (char c in s) { IScope local = new ScopeChain(bodyScope); PatternAssign assign = new PatternAssign(map, local, true/*bCreate*/); assign.Assign(new ValueString(c.ToString())); result = EvalBody.Do(valueBody, local); local.Exit(); } } else if (collection is ValueArray) { List<Value> list = collection.AsArray; foreach (Value v in list) { IScope local = new ScopeChain(bodyScope); PatternAssign assign = new PatternAssign(map, local, true/*bCreate*/); assign.Assign(v); result = EvalBody.Do(valueBody, local); local.Exit(); } } else if (collection is ValueMap) { Dictionary<string, Value> dict = collection.AsMap.Raw; foreach (string key in dict.Keys) { List<Value> list = new List<Value>(); list.Add(new ValueString(key)); list.Add(dict[key]); IScope local = new ScopeChain(bodyScope); PatternAssign assign = new PatternAssign(map, local, true/*bCreate*/); assign.Assign(new ValueArray(list)); result = EvalBody.Do(valueBody, local); local.Exit(); } } else if (collection is ValueLine) { List<DelimiterList> list = collection.AsLine; // if delimiter is specified, wrap each line w/ it ValueDelimiter delim = scope.GetValue(new Token(map["delim"].AsString)) as ValueDelimiter; if (delim != null) { List<DelimiterList> delimList = new List<DelimiterList>(); int indent = (list.Count > 0 ? list[0].Indent : 0); foreach (DelimiterList line in list) { DelimiterList newLine = line; // wrap lines & nested lines with proper delimiter, except for nested values that get evaled if (line.Indent == indent || (delim.DelimiterType != DelimiterType.AsValue && line.Indent >= indent)) { List<DelimiterNode> nodes = new List<DelimiterNode>(); string original = line.Original; if (delim.DelimiterType == DelimiterType.AsString && line.Indent > indent) { // put the indentation back if portions of the body were indented original = ""; for (int i = 0; i < line.Indent - indent; i++) original += " "; original += line.Original; } nodes.Add(new DelimiterNodeList(new DelimiterList(delim, line.Nodes, line.Indent, "", original, line.Scope))); newLine = new DelimiterList(delim, nodes, indent, "", original, line.Scope); } delimList.Add(newLine); } list = delimList; } // for each line, eval it then eval the body ILineRequestor lines = new LineConsumer(list); while (lines.HasCurrent()) { IScope local = new ScopeChain(bodyScope); Value value = EvalLines.DoOne(lines, local); PatternAssign assign = new PatternAssign(map, local, true/*bCreate*/); assign.Assign(value); result = EvalBody.Do(valueBody, local); local.Exit(); } } return result; }
/// <summary> /// Get the scope to modify based on values in 'map'. /// :level = the number of parents up the chain to modify /// :map = if present, the map to modify /// </summary> internal static IScope GetScopeToModify(Map map, IScope scope, bool bIncludeMap) { if (!map.ContainsKey("map") && !map.ContainsKey("level")) return scope; ValueMap valueMap = (bIncludeMap ? map["map"] as ValueMap : null); int level = (map.ContainsKey("level") ? map["level"].AsInt : 0); // scope we're going to modify IScope toModify = scope; if (valueMap != null) toModify = new ScopeChain(valueMap.AsMap); else for (int i = 0; i < level && toModify.Parent != null; i++) toModify = toModify.Parent; if (toModify == null) toModify = scope; return toModify; }
public void TestParamMetadata2() { try { IScope scope = new ScopeChain(GetBootstrapScope()); { ScopeChain nested = new ScopeChain(scope); Value value = TestSupport.ToValue("{ :a :a :remainder ( :remainder ... ) } v= { :a 4 :b 5 :c 6 }", nested); // value should be { :a 4 :b 5 :c 6 } Assert.AreEqual(3, value.AsMap.Count); // nested should now contain "a" and "remainder" Assert.AreEqual(4, nested.GetValue(new Token("a")).AsInt); Map rest = nested.GetValue(new Token("remainder")).AsMap; Assert.AreEqual(2, rest.Count); Assert.AreEqual(5, rest["b"].AsInt); Assert.AreEqual(6, rest["c"].AsInt); } { ScopeChain nested = new ScopeChain(scope); Value value = TestSupport.ToValue("[ ->a ( ->remainder ... ) ] v= { :a 4 :b 5 :c 6 }", nested); // value should be { :a 4 :b 5 :c 6 } Assert.AreEqual(3, value.AsMap.Count); // nested should now contain "a" and "remainder" Assert.AreEqual(4, nested.GetValue(new Token("a")).AsInt); Map rest = nested.GetValue(new Token("remainder")).AsMap; Assert.AreEqual(2, rest.Count); Assert.AreEqual(5, rest["b"].AsInt); Assert.AreEqual(6, rest["c"].AsInt); } { ScopeChain nested = new ScopeChain(scope); Value value = TestSupport.ToValue("[ ->a ( ->remainder ... ) ] v= [ 7 8 9 ]", nested); // value should be [ 7 8 9 ] Assert.AreEqual(3, value.AsArray.Count); // nested should now contain "a" and "remainder" Assert.AreEqual(7, nested.GetValue(new Token("a")).AsInt); System.Collections.Generic.List<Value> rest = nested.GetValue(new Token("remainder")).AsArray; Assert.AreEqual(2, rest.Count); Assert.AreEqual(8, rest[0].AsInt); Assert.AreEqual(9, rest[1].AsInt); } } catch (Loki3Exception e) { Assert.Fail(e.ToString()); } }
public void TestParamMetadata() { try { IScope scope = new ScopeChain(GetBootstrapScope()); string[] lines = { ":addUp v= func1 [ ( ->a : :int ) ( ->b d= 5 ) ]", " a + b", }; LineConsumer requestor = new LineConsumer(lines); EvalLines.Do(requestor, scope); { // pass expected parameters Value value = TestSupport.ToValue("addUp [ 1 2 ]", scope); Assert.AreEqual(3, value.AsInt); } { // leave off 2nd, use default Value value = TestSupport.ToValue("addUp [ 1 ]", scope); Assert.AreEqual(6, value.AsInt); } // it throws if we pass wrong type bool bThrew = false; try { TestSupport.ToValue("addUp [ true 1 ]", scope); } catch (Loki3Exception) { bThrew = true; } Assert.IsTrue(bThrew); } catch (Loki3Exception e) { Assert.Fail(e.ToString()); } }
static IScope CreateMathScope() { ScopeChain scope = new ScopeChain(); Math.Register(scope); ValueDelimiter square = new ValueDelimiter("]", DelimiterType.AsArray); scope.SetValue("[", square); return scope; }
static IScope CreateScope() { ScopeChain scope = new ScopeChain(); MapFunctions.Register(scope); scope.SetValue("create-map", new CreateMap()); scope.SetValue("[", new ValueDelimiter("]", DelimiterType.AsArray)); scope.SetValue("{", new ValueDelimiter("}", DelimiterType.AsArray, new CreateMap())); return scope; }
internal override Value Eval(Value arg, IScope scope) { Map map = arg.AsMap; IScope theScope = Utility.GetScopeToModify(map, scope, false/*bIncludeMap*/); if (map["createChild?"].AsBool) theScope = new ScopeChain(theScope); return theScope.AsValue; }
public void TestSetValue() { IScope scope = CreateValueScope(); { Value value = TestSupport.ToValue("l3.setValue { :key :a :value 5 }", scope); Assert.AreEqual(5, value.AsInt); Value fetch = scope.GetValue(new Token("a")); Assert.AreEqual(5, fetch.AsInt); } { // change a Value value = TestSupport.ToValue("l3.setValue { :key :a :value 7.5 }", scope); Assert.AreEqual(7.5, value.AsFloat); Value fetch = scope.GetValue(new Token("a")); Assert.AreEqual(7.5, fetch.AsFloat); } { // set an array Value value = TestSupport.ToValue("l3.setValue { :key :key :value [ a 2 false ] }", scope); List<Value> array = value.AsArray; Assert.AreEqual(3, array.Count); Assert.AreEqual(7.5, array[0].AsFloat); Assert.AreEqual(2, array[1].AsInt); Assert.AreEqual(false, array[2].AsBool); Value fetch = scope.GetValue(new Token("key")); List<Value> fetchArray = fetch.AsArray; Assert.AreEqual(3, fetchArray.Count); Assert.AreEqual(7.5, fetchArray[0].AsFloat); Assert.AreEqual(2, fetchArray[1].AsInt); Assert.AreEqual(false, fetchArray[2].AsBool); } // create a nested scope IScope nested = new ScopeChain(scope); { // explicitly say create-if-needed TestSupport.ToValue("l3.setValue { :key :created :value 3 :create? true }", nested); Value fetch = nested.GetValue(new Token("created")); Assert.AreEqual(3, fetch.AsInt); } { // asking to reuse non-existent var throws bool bThrew = false; try { Value value = TestSupport.ToValue("l3.setValue { :key :doesnt-exist :value 3 :create? false }", nested); } catch (Loki3Exception e) { bThrew = true; Assert.AreEqual("doesnt-exist", e.Errors[Loki3Exception.keyBadToken].AsString); } Assert.IsTrue(bThrew); } { // set var on parent scope & reuse on nested scope TestSupport.ToValue("l3.setValue { :key :on-parent :value 5 :create? true }", scope); Assert.AreEqual(5, scope.AsMap["on-parent"].AsInt); TestSupport.ToValue("l3.setValue { :key :on-parent :value 4 :create? false }", nested); Assert.AreEqual(4, scope.AsMap["on-parent"].AsInt); Assert.AreNotEqual(null, nested.Exists("on-parent")); Assert.IsFalse(nested.AsMap.ContainsKey("on-parent")); } { // set a value on a map Map map = new Map(); map["one"] = new ValueInt(1); map["two"] = new ValueInt(2); ValueMap valueMap = new ValueMap(map); scope.SetValue("mymap", valueMap); Value value = TestSupport.ToValue("l3.setValue { :key :one :value 3 :map mymap }", scope); Assert.AreEqual(3, value.AsInt); Assert.AreEqual(3, map["one"].AsInt); } { // pattern matching: extract values out of an array Value value = TestSupport.ToValue("l3.setValue { :key [ :first :second :third ] :value [ 11 22 33 ] :create? true }", scope); Assert.AreEqual(11, scope.AsMap["first"].AsInt); Assert.AreEqual(22, scope.AsMap["second"].AsInt); Assert.AreEqual(33, scope.AsMap["third"].AsInt); } { // special case: initialize every token in array to nil Value value = TestSupport.ToValue("l3.setValue { :key [ :first :second :third ] :value nil :create? true }", scope); Assert.IsTrue(scope.AsMap["first"].IsNil); Assert.IsTrue(scope.AsMap["second"].IsNil); Assert.IsTrue(scope.AsMap["third"].IsNil); } { // if initOnly?, don't overwrite existing values Value value = TestSupport.ToValue("l3.setValue { :key [ :aa :bb ] :value [ 2 3 ] :create? true :initOnly? true }", scope); Assert.AreEqual(2, scope.AsMap["aa"].AsInt); Assert.AreEqual(3, scope.AsMap["bb"].AsInt); value = TestSupport.ToValue("l3.setValue { :key [ :aa :bb ] :value [ 4 5 ] :create? true :initOnly? true }", scope); Assert.AreEqual(2, scope.AsMap["aa"].AsInt); Assert.AreEqual(3, scope.AsMap["bb"].AsInt); value = TestSupport.ToValue("l3.setValue { :key [ :bb :cc ] :value nil :create? true :initOnly? true }", scope); Assert.AreEqual(3, scope.AsMap["bb"].AsInt); Assert.IsTrue(scope.AsMap["cc"].IsNil); } { // if returnSuccess?, return value tells whether a value was set Value value = TestSupport.ToValue("l3.setValue { :key [ :x :y ] :value [ 2 3 ] :returnSuccess? true }", scope); Assert.IsTrue(value.AsBool); value = TestSupport.ToValue("l3.setValue { :key [ :x :y :z ] :value [ 2 3 ] :returnSuccess? true }", scope); Assert.IsFalse(value.AsBool); } }
static IScope CreateValueScope() { ScopeChain scope = new ScopeChain(); Values.Register(scope); scope.SetValue("(", new ValueDelimiter(")", DelimiterType.AsValue)); scope.SetValue("[", new ValueDelimiter("]", DelimiterType.AsArray)); scope.SetValue("'", new ValueDelimiter("'", DelimiterType.AsString)); scope.SetValue("{", new ValueDelimiter("}", DelimiterType.AsArray, new CreateMap())); scope.SetValue("`", new ValueDelimiter("`", DelimiterType.AsRaw)); return scope; }
public void TestLoop() { try { IScope scope = new ScopeChain(GetBootstrapScope()); { // l3.loop string[] lines = { ":total v= 0", ":i v= 0", "l3.loop .{ :check .` i !=? 5", " :i = i + 1", " :total = total + i", }; LineConsumer requestor = new LineConsumer(lines); Value result = EvalLines.Do(requestor, scope); Assert.AreEqual(15, result.AsInt); } } catch (Loki3Exception e) { Assert.Fail(e.ToString()); } }
public void TestNested() { try { IScope scope = new ScopeChain(GetBootstrapScope()); string[] lines = { ":result v= 0", "if flag1?", " if flag2?", " :result = 1", " else", " :result = 2", "else", " if flag3?", " :result = 3", " else", " :result = 4", }; { // nested if block is run scope.SetValue("flag1?", ValueBool.True); scope.SetValue("flag2?", ValueBool.True); LineConsumer requestor = new LineConsumer(lines); EvalLines.Do(requestor, scope); Assert.AreEqual(1, scope.GetValue(new Token("result")).AsInt); } { // nested else is run scope.SetValue("flag1?", ValueBool.True); scope.SetValue("flag2?", ValueBool.False); LineConsumer requestor = new LineConsumer(lines); EvalLines.Do(requestor, scope); Assert.AreEqual(2, scope.GetValue(new Token("result")).AsInt); } { // top level else block is run, nested if scope.SetValue("flag1?", ValueBool.False); scope.SetValue("flag3?", ValueBool.True); LineConsumer requestor = new LineConsumer(lines); EvalLines.Do(requestor, scope); Assert.AreEqual(3, scope.GetValue(new Token("result")).AsInt); } { // top level else block is run, nested else scope.SetValue("flag1?", ValueBool.False); scope.SetValue("flag3?", ValueBool.False); LineConsumer requestor = new LineConsumer(lines); EvalLines.Do(requestor, scope); Assert.AreEqual(4, scope.GetValue(new Token("result")).AsInt); } } catch (Loki3Exception e) { Assert.Fail(e.ToString()); } }
internal override Value Eval(DelimiterNode prev, DelimiterNode next, IScope paramScope, IScope bodyScope, INodeRequestor nodes, ILineRequestor requestor) { // if we need prev or next params but they're not there, simply return the function if ((m_usePrevious || m_useNext) && prev == null && next == null) return this; // scope we'll add prev/next/body params to ScopeChain localScope = new ScopeChain(); if (m_usePrevious) { if (prev == null) { if (m_useNext) return new PartialFunctionIn(this, null, next); throw new Loki3Exception().AddMissingValue(true/*bPrevious*/); } Value value1 = ComputeParam(Metadata[keyPreviousPattern], prev, paramScope, nodes, requestor); Value match, leftover; if (!PatternChecker.Do(value1, Metadata[keyPreviousPattern], false/*bShortPat*/, out match, out leftover)) throw new Loki3Exception().AddWrongPattern(Metadata[keyPreviousPattern], value1); if (leftover != null) { if (m_useNext) // currently can't do partials for infix throw new Loki3Exception().AddWrongPattern(Metadata[keyPreviousPattern], value1); // create a partial function that starts w/ match & still needs leftover return new UserFunction(this, match, leftover, null); } Utility.AddToScope(m_pattern1, match, localScope); } if (m_useNext) { if (next == null) { if (m_usePrevious) return new PartialFunctionIn(this, prev, null); throw new Loki3Exception().AddMissingValue(false/*bPrevious*/); } Value value2 = ComputeParam(Metadata[keyNextPattern], next, paramScope, nodes, requestor); Value match, leftover; if (!PatternChecker.Do(value2, Metadata[keyNextPattern], false/*bShortPat*/, out match, out leftover)) throw new Loki3Exception().AddWrongPattern(Metadata[keyNextPattern], value2); // if we created a function that needs a body, add it if present ValueFunction matchFunc = match as ValueFunction; if (matchFunc != null && matchFunc.RequiresBody() && requestor != null) match = EvalList.DoAddBody(matchFunc, bodyScope, requestor); if (leftover != null) { if (m_usePrevious) // currently can't do partials for infix throw new Loki3Exception().AddWrongPattern(Metadata[keyNextPattern], value2); // create a partial function that starts w/ match & still needs leftover return new UserFunction(this, match, null, leftover); } Utility.AddToScope(m_pattern2, match, localScope); } // tack on body if requested if (Metadata.GetOptionalT<bool>("body?", false)) { bool foundBody = false; // if there's a following node, use it DelimiterNode possibleBody = nodes.GetNext(); if (possibleBody != null) { localScope.SetValue("body", UseNodeAsBody(possibleBody)); foundBody = true; } // if no body, use the following lines if (!foundBody && requestor != null) { List<DelimiterList> body = EvalList.DoGetBody(bodyScope, requestor); if (body.Count != 0) { localScope.SetValue("body", new ValueLine(body, bodyScope)); foundBody = true; } } if (!foundBody) // create a partial function that only needs a body return new UserFunction(this, localScope); } // create a new scope and add passed in arguments... ScopeChain scope = (ShouldCreateScope ? new ScopeChain(bodyScope) : bodyScope as ScopeChain); scope.Function = this; if (m_fullPattern != null && m_passed != null) Utility.AddToScope(m_fullPattern, m_passed, scope); // ...and the prev/next/body params we just extracted Utility.AddToScope(localScope, scope); if (m_passedScope != null) Utility.AddToScope(m_passedScope, scope); // lazily parse EnsureParsed(bodyScope); // eval each line using current scope try { Value retval = EvalBody.Do(m_parsedLines, scope); scope.Exit(); return retval; } catch (PopStackException pop) { // if we're supposed to pop back to here then return, else keep throwing up the stack if (pop.ScopeName == scope.Name) return pop.Return; throw pop; } catch (Loki3Exception e) { // if this scope catches exceptions, stop here if (Loki3Exception.catchScopeName == scope.Name) { if (scope.Parent != null) scope.Parent.SetValue(Loki3Exception.exceptionKey, new ValueMap(e.Errors)); return ValueNil.Nil; } throw e; } }
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); } }