/// <summary>Read and eval all the lines in a file</summary> internal static void Do(string file, IScope scope) { StreamReader stream = null; try { List<string> lines = new List<string>(); stream = new StreamReader(file); while (!stream.EndOfStream) lines.Add(stream.ReadLine()); LineConsumer consumer = new LineConsumer(lines); EvalLines.Do(consumer, scope); stream.Close(); } catch (Loki3Exception e) { e.AddFileName(file); if (stream != null) stream.Close(); throw e; } catch (System.Exception e) { if (stream != null) stream.Close(); throw e; } }
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()); } }
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; }
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()); } }
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()); } }
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 TestGetFunctionBody() { IScope scope = CreateValueScope(); { // built-in function doesn't have a body Value value = TestSupport.ToValue("l3.getFunctionBody { :key :l3.getFunctionBody }", scope); Assert.IsTrue(value.IsNil); } { // get user defined function body string[] lines = { "l3.setValue { :key :v= :value ( l3.createFunction { :pre ->key :post ->value :order 5 :body [ ` l3.setValue { :key key :value value :level 1 :create? true } ` ] } ) }", ":myFunc v= l3.createFunction { :post ->a }", " some code", " more code", }; LineConsumer requestor = new LineConsumer(lines); EvalLines.Do(requestor, scope); Value value = TestSupport.ToValue("l3.getFunctionBody { :key :myFunc }", scope); Assert.AreEqual(2, value.Count); value = TestSupport.ToValue("l3.getFunctionBody { :function ( myFunc ) }", scope); Assert.AreEqual(2, value.Count); } }
/// <summary> /// Start a read-eval-print loop at the console /// </summary> /// <param name="scope">scope for parse-eval</param> internal static void Do(IScope scope, string prompt) { prompt += " "; string s = ""; do { Console.Write(prompt); // keep reading lines as long as they end with \ List<string> lines = new List<string>(); bool bMore = false; do { s = Console.ReadLine(); bMore = (s.Length > 2 && s[s.Length - 1] == '\\' && s[s.Length - 2] == ' '); if (bMore) s = s.Substring(0, s.Length - 2); lines.Add(s); } while (bMore); LineConsumer consumer = new LineConsumer(lines); // eval the line(s) try { Value v = EvalLines.Do(consumer, scope); Console.WriteLine(v.ToString()); } catch (Loki3Exception error) { // if we're at the root scope, remove it to avoid infinite // recursion in Map.ToString if (error.Errors.ContainsKey("l3.error.scope")) if (error.Errors["l3.error.scope"].AsMap == scope.AsMap) error.Errors.Raw.Remove("l3.error.scope"); if (scope.Exists("prettify") != null) { scope.SetValue("lastError", new ValueMap(error.Errors)); Value v = loki3.builtin.test.TestSupport.ToValue("prettify lastError", scope); Console.WriteLine("LOKI3 ERROR:\n" + v.AsString); if (error.Errors.ContainsKey(Loki3Exception.keyScope)) { scope.SetValue("lastScope", error.Errors[Loki3Exception.keyScope]); try { v = loki3.builtin.test.TestSupport.ToValue("dumpStack lastScope", scope); Console.WriteLine("STACK:\n" + v.AsString); } catch (Exception e) { Console.WriteLine("ERROR PRINTING STACK:\n" + e.ToString()); } } } else { Console.WriteLine(error.ToString()); } } catch (Exception error) { Console.WriteLine("INTERNAL ERROR: " + error.ToString()); } } while (s != ""); }