public void VargsOnly() { var co = new CodeObject(new byte[0]); co.ArgCount = 0; co.Defaults = new List <object>(); co.VarNames.Add("args"); co.Flags |= CodeObject.CO_FLAGS_VARGS; var inputs = new object[][] { new object[] { 1 }, new object[] { }, }; var keywordsIn = new Dictionary <string, object>[] { null, null, }; var outputs = new object[][] { new object[] { PyTuple.Create(new object[] { 1 }) }, new object[] { PyTuple.Create(new object[0]) } }; InOutTest(co, inputs, keywordsIn, outputs); }
public void VargsKeywordDefaults() { var co = new CodeObject(new byte[0]); co.ArgCount = 0; co.Defaults = new List <object>(); co.VarNames.Add("args"); co.VarNames.Add("a"); co.VarNames.Add("b"); co.Defaults = new List <object>(); co.KWDefaults = new List <object>(); co.KWDefaults.Add(3); co.KWDefaults.Add(4); co.KWOnlyArgCount = 2; co.Flags |= CodeObject.CO_FLAGS_VARGS; var inputs = new object[][] { new object[] { 1, 2 }, new object[] { 1, 2 }, new object[] { 1, 2 }, new object[] { 1, 2 }, new object[] { }, new object[] { }, }; var keywordsIn = new Dictionary <string, object>[] { new Dictionary <string, object> { }, new Dictionary <string, object> { { "a", 200 }, { "b", 100 } }, new Dictionary <string, object> { { "a", 200 }, }, new Dictionary <string, object> { { "b", 100 }, }, new Dictionary <string, object> { { "a", 200 }, }, new Dictionary <string, object> { }, }; var outputs = new object[][] { new object[] { PyTuple.Create(new object[] { 1, 2 }), 3, 4 }, new object[] { PyTuple.Create(new object[] { 1, 2 }), 200, 100 }, new object[] { PyTuple.Create(new object[] { 1, 2 }), 200, 4 }, new object[] { PyTuple.Create(new object[] { 1, 2 }), 3, 100 }, new object[] { PyTuple.Create(new object[0]), 200, 4 }, new object[] { PyTuple.Create(new object[0]), 3, 4 }, }; InOutTest(co, inputs, keywordsIn, outputs); }
public void ArgsDefaultsVargs() { var co = new CodeObject(new byte[0]); co.ArgCount = 4; co.Defaults = new List <object>(); co.VarNames.Add("a"); co.VarNames.Add("b"); co.VarNames.Add("c"); co.VarNames.Add("d"); co.VarNames.Add("e"); co.Defaults = new List <object>(); co.Defaults.Add(-1); co.Defaults.Add(-2); co.Flags |= CodeObject.CO_FLAGS_VARGS; // The 5th case is commented out because it's actually illegal Python. I stubbed my toe on it when I created the arg param // matcher state machine. It choked there and I verified in Python that it wouldn't have worked. var inputs = new object[][] { new object[] { 1, 2, 3, 4, 5 }, new object[] { 6, 7, 8, 9 }, new object[] { 10, 11, 12 }, new object[] { 13, 14 }, //new object[] { }, new object[] { }, }; var keywordsIn = new Dictionary <string, object>[] { null, null, null, null, //new Dictionary<string, object> { { "a", 15 }, { "b", 16 }, { "c", 17 }, { "d", 18 }, { "e", PyTuple.Create(new object[] { 19 }) } }, new Dictionary <string, object> { { "a", 15 }, { "b", 16 }, { "c", 17 }, { "d", 18 } }, }; var outputs = new object[][] { new object[] { 1, 2, 3, 4, PyTuple.Create(new object[] { 5 }) }, new object[] { 6, 7, 8, 9, PyTuple.Create(new object[0]) }, new object[] { 10, 11, 12, -2, PyTuple.Create(new object[0]) }, new object[] { 13, 14, -1, -2, PyTuple.Create(new object[0]) }, //new object[] { 15, 16, 17, 18, PyTuple.Create(new object[] { 19 }) }, new object[] { 15, 16, 17, 18, PyTuple.Create(new object[0]) }, }; InOutTest(co, inputs, keywordsIn, outputs); }
public async Task LenFunction() { var dictin = PyDict.Create(); dictin.InternalDict[PyString.Create("1")] = PyInteger.Create(1); dictin.InternalDict[PyString.Create("2")] = PyInteger.Create(2); await runBasicTest( "listout = len(listin)\n" + "dictout = len(dictin)\n" + "tupleout = len(tuplein)\n" + "strout = len(strin)\n" + "rangeout = len(rangein)\n" + "arrayout = len(arrayin)\n" + "enumerableout = len(enumerablein)\n" + "dotnetstrout = len(dotnetstrin)\n", // I think this should be IEnumerable but I'm not taking chances new Dictionary <string, object>() { { "listin", PyList.Create(new List <object>() { PyInteger.Create(1) }) }, { "dictin", dictin }, { "tuplein", PyTuple.Create(new object[] { 1, 2, 3 }) }, { "strin", PyString.Create("1234") }, { "rangein", PyRange.Create(5, 0, 1) }, { "arrayin", new int[] { 1, 2, 3, 4, 5, 6 } }, { "enumerablein", new List <int>() { 1, 2, 3, 4, 5, 6, 7 } }, { "dotnetstrin", "12345678" }, }, new VariableMultimap(new TupleList <string, object> { { "listout", PyInteger.Create(1) }, { "dictout", PyInteger.Create(2) }, { "tupleout", PyInteger.Create(3) }, { "strout", PyInteger.Create(4) }, { "rangeout", PyInteger.Create(5) }, { "arrayout", PyInteger.Create(6) }, { "enumerableout", PyInteger.Create(7) }, { "dotnetstrout", PyInteger.Create(8) }, }), 1); }
// TODO [KEYWORD-POSITIONAL-ONLY] Implement positional-only (/) and keyword-only (*) arguments // TODO [ARGPARAMMATCHER ERRORS] Generate errors when input arguments don't match requirements of code object. // TODO [**kwargs] Support kwargs public static object[] Resolve(CodeObject co, object[] inArgs, Dictionary <string, object> keywords = null) { // This state machine model is definitely more verbose but it's much easier to maintain because: // 1. We will loiter in only one section at a time so we can mentally compartmentalize what's happening. // 2. We can be very explicit about what happens next. var state = ArgParamState.Initial; int inArg_i = 0; int outArg_i = 0; int default_i = 0; object[] outArgs = new object[co.ArgCount + co.KWOnlyArgCount + (co.HasVargs ? 1 : 0)]; bool hasDefaults = co.Defaults.Count > 0; bool hasKwOnlyArgs = co.KWOnlyArgCount > 0; bool hasKeywords = keywords is null ? false : true; // Determine our true first state. Initial is just symbolic. if (co.ArgCount > 0) { state = ArgParamState.Positional; if (inArgs.Length == 0) { if (hasDefaults || hasKeywords) { state = ArgParamState.KeywordOverride; } else if (co.HasVargs) { state = ArgParamState.Variable; } else if (hasKwOnlyArgs) { state = ArgParamState.KeywordOnly; } else { state = ArgParamState.Finished; } } } else { if (co.HasVargs) { state = ArgParamState.Variable; } else if (hasKwOnlyArgs) { state = ArgParamState.KeywordOnly; } else { state = ArgParamState.Finished; } } if (state == ArgParamState.Initial) { throw new InvalidOperationException("Arg param state was still in initial state after checking entire environment."); } while (state != ArgParamState.Finished) { switch (state) { case ArgParamState.Positional: while (inArg_i < inArgs.Length && inArg_i < co.ArgCount) { outArgs[outArg_i] = inArgs[inArg_i]; outArg_i += 1; inArg_i += 1; } if (inArg_i < co.ArgCount) { if (hasKeywords || hasDefaults) { state = ArgParamState.KeywordOverride; } else if (co.HasVargs) { state = ArgParamState.Variable; } else { // TODO: [PARAM MATCHER ERRORS] Cast to actual TypeError throw new Exception("TypeError: " + co.Name + " takes " + co.ArgCount + " position arguments but " + inArgs.Length + " were given"); } } else if (co.HasVargs) { state = ArgParamState.Variable; } else if (hasKwOnlyArgs) { state = ArgParamState.KeywordOnly; } else { state = ArgParamState.Finished; } break; case ArgParamState.KeywordOverride: { List <string> missingOverrides = null; while (inArg_i < co.ArgCount - co.Defaults.Count) { if (hasKeywords && keywords.ContainsKey(co.VarNames[outArg_i])) { outArgs[outArg_i] = keywords[co.VarNames[outArg_i]]; } else { if (missingOverrides == null) { missingOverrides = new List <string>(); } missingOverrides.Add(co.VarNames[outArg_i]); } ++inArg_i; ++outArg_i; } if (missingOverrides != null && missingOverrides.Count > 0) { // TODO: [PARAM MATCHER ERRORS] Cast to actual TypeError var errorBuilder = new StringBuilder("TypeError: " + co.Name + " missing " + co.ArgCount + " positional argument"); if (missingOverrides.Count == 1) { errorBuilder.Append(": "); errorBuilder.Append(missingOverrides[0]); } else { errorBuilder.Append("s: "); for (int i = 0; i < missingOverrides.Count - 2; ++i) { errorBuilder.Append(missingOverrides[i]); errorBuilder.Append(", "); } errorBuilder.Append(missingOverrides[missingOverrides.Count - 2]); errorBuilder.Append(" and "); errorBuilder.Append(missingOverrides[missingOverrides.Count - 1]); } throw new Exception(errorBuilder.ToString()); } else if (co.Defaults.Count > 0) { state = ArgParamState.KeywordOrDefault; } else if (co.HasVargs) { state = ArgParamState.Variable; } else if (co.HasKwargs) { state = ArgParamState.KeywordOnly; } else { state = ArgParamState.Finished; } } break; case ArgParamState.KeywordOrDefault: // We might not start at the first default; some positionals might have had a keyword override. default_i = outArg_i - (co.ArgCount - co.Defaults.Count); while (default_i < co.Defaults.Count || outArg_i < co.ArgCount) { if (hasKeywords && keywords.ContainsKey(co.VarNames[outArg_i])) { outArgs[outArg_i] = keywords[co.VarNames[outArg_i]]; } else if (default_i < co.Defaults.Count) { outArgs[outArg_i] = co.Defaults[default_i]; } else { // TODO: [PARAM MATCHER ERRORS] Cast to actual TypeError throw new Exception("TypeError: " + co.Name + " takes " + co.ArgCount + " position arguments but " + inArgs.Length + " were given"); } default_i += 1; outArg_i += 1; } if (default_i >= co.Defaults.Count) { if (co.HasVargs) { state = ArgParamState.Variable; } else if (hasKwOnlyArgs) { state = ArgParamState.KeywordOnly; } else { state = ArgParamState.Finished; } } break; case ArgParamState.Variable: { var vargLength = inArgs.Length - inArg_i; vargLength = vargLength < 0 ? 0 : vargLength; object[] vargs = new object[vargLength]; int varg_i = 0; // Variable arguments (*args) while (inArg_i < inArgs.Length) { vargs[varg_i] = inArgs[inArg_i]; ++inArg_i; ++varg_i; } outArgs[outArg_i] = PyTuple.Create(vargs); outArg_i += 1; if (hasKwOnlyArgs) { state = ArgParamState.KeywordOnly; } else { state = ArgParamState.Finished; } } break; case ArgParamState.KeywordOnly: { int kwonly_i = 0; while (kwonly_i < co.KWDefaults.Count) { if (hasKeywords && keywords.ContainsKey(co.VarNames[outArg_i])) { outArgs[outArg_i] = keywords[co.VarNames[outArg_i]]; } else { outArgs[outArg_i] = co.KWDefaults[kwonly_i]; } ++kwonly_i; ++outArg_i; } } state = ArgParamState.Finished; break; } } return(outArgs); }