/// <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; }
internal override Value Eval(Value arg, IScope scope) { Map map = arg.AsMap; // get parameter Value value = map["function"]; if (value.IsNil) { Value valueKey = map["key"]; if (!valueKey.IsNil) { string key = valueKey.AsString; IScope container = scope.Exists(key); if (container == null) throw new Loki3Exception().AddMissingKey(map["key"]); value = container.GetValue(new Token(key)); } } if (value.IsNil) throw new Loki3Exception().AddMissingKey(new ValueString("function")); // fetch body of function ValueFunction function = value as ValueFunction; if (function == null) throw new Loki3Exception().AddWrongType(ValueType.Function, value.Type); List<DelimiterList> body = function.GetBody(scope); if (body == null) return ValueNil.Nil; // make an array List<Value> array = new List<Value>(); foreach (DelimiterList line in body) { // attach scope to the value, preferring the lines's scope // & falling back to the function's scope if not present IScope rawScope = (line.Scope != null ? line.Scope : scope); array.Add(new ValueRaw(line, rawScope)); } return new ValueArray(array); }
/// <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 != ""); }