static MalVal EVAL(MalVal orig_ast, Dictionary <string, MalVal> env) { MalVal a0; //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return(eval_ast(orig_ast, env)); } // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return(ast); } a0 = ast[0]; if (!(a0 is MalSymbol)) { throw new Mal.types.MalError("attempt to apply on non-symbol '" + Mal.printer._pr_str(a0, true) + "'"); } var el = (MalList)eval_ast(ast, env); var f = (MalFunc)el[0]; return(f.apply(el.rest())); }
static MalVal eval_ast(MalVal ast, Env env) { if (ast is MalSymbol) { return(env.get((MalSymbol)ast)); } else if (ast is MalList) { MalList old_lst = (MalList)ast; MalList new_lst = ast.list_Q() ? new MalList() : (MalList) new MalVector(); foreach (MalVal mv in old_lst.getValue()) { new_lst.conj_BANG(EVAL(mv, env)); } return(new_lst); } else if (ast is MalHashMap) { var new_dict = new Dictionary <string, MalVal>(); foreach (var entry in ((MalHashMap)ast).getValue()) { new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env)); } return(new MalHashMap(new_dict)); } else { return(ast); } }
// eval static MalVal EVAL(MalVal orig_ast, Dictionary <string, MalVal> env) { MalVal a0; // Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (orig_ast is MalSymbol) { MalSymbol sym = (MalSymbol)orig_ast; return((MalVal)env[sym.getName()]); } else if (orig_ast is MalVector) { MalVector old_lst = (MalVector)orig_ast; MalVector new_lst = new MalVector(); foreach (MalVal mv in old_lst.getValue()) { new_lst.conj_BANG(EVAL(mv, env)); } return(new_lst); } else if (orig_ast is MalHashMap) { var new_dict = new Dictionary <string, MalVal>(); foreach (var entry in ((MalHashMap)orig_ast).getValue()) { new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env)); } return(new MalHashMap(new_dict)); } else if (!(orig_ast is MalList)) { return(orig_ast); } // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return(ast); } a0 = ast[0]; if (!(a0 is MalSymbol)) { throw new Mal.types.MalError("attempt to apply on non-symbol '" + Mal.printer._pr_str(a0, true) + "'"); } MalFunc f = (MalFunc)EVAL(ast[0], env); MalList arguments = new MalList(); foreach (MalVal mv in ast.rest().getValue()) { arguments.conj_BANG(EVAL(mv, env)); } return(f.apply(arguments)); }
private static List <object> MapMalListToDotNetList(MalList malList) { List <object> dotNetList = new List <object>(); foreach (MalVal malVal in malList.getValue()) { dotNetList.Add(MapMalToDotNet(malVal)); } return(dotNetList); }
static MalVal EVAL(MalVal orig_ast, Env env) { MalVal a0, a1, a2, res; MalList el; //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return(eval_ast(orig_ast, env)); } // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return(ast); } a0 = ast[0]; if (!(a0 is MalSymbol)) { throw new Mal.types.MalError("attempt to apply on non-symbol '" + Mal.printer._pr_str(a0, true) + "'"); } switch (((MalSymbol)a0).getName()) { case "def!": a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); env.set((MalSymbol)a1, res); return(res); case "let*": a1 = ast[1]; a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for (int i = 0; i < ((MalList)a1).size(); i += 2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i + 1]; let_env.set(key, EVAL(val, let_env)); } return(EVAL(a2, let_env)); default: el = (MalList)eval_ast(ast, env); var f = (MalFunc)el[0]; return(f.apply(el.rest())); } }
public Env(Env outer, MalList binds, MalList exprs) { this.outer = outer; for (int i=0; i<binds.size(); i++) { string sym = ((MalSymbol)binds.nth(i)).getName(); if (sym == "&") { data[((MalSymbol)binds.nth(i+1)).getName()] = exprs.slice(i); break; } else { data[sym] = exprs.nth(i); } } }
// eval public static bool starts_with(MalVal ast, string sym) { if (ast is MalList && !(ast is MalVector)) { MalList list = (MalList)ast; if (list.size() == 2 && list[0] is MalSymbol) { MalSymbol a0 = (MalSymbol)list[0]; return(a0.getName() == sym); } } return(false); }
public Env(Env outer, MalList binds, MalList exprs) { this.outer = outer; for (int i = 0; i < binds.size(); i++) { string sym = ((MalSymbol)binds.nth(i)).getName(); if (sym == "&") { data[((MalSymbol)binds.nth(i + 1)).getName()] = exprs.slice(i); break; } else { data[sym] = exprs.nth(i); } } }
public static MalVal qq_loop(MalList ast) { MalVal acc = new MalList(); for (int i = ast.size() - 1; 0 <= i; i -= 1) { MalVal elt = ast[i]; if (starts_with(elt, "splice-unquote")) { acc = new MalList(new MalSymbol("concat"), ((MalList)elt)[1], acc); } else { acc = new MalList(new MalSymbol("cons"), quasiquote(elt), acc); } } return(acc); }
public static MalVal read_list(Reader rdr, MalList lst, char start, char end) { string token = rdr.next(); if (token[0] != start) { throw new ParseError("expected '" + start + "'"); } while ((token = rdr.peek()) != null && token[0] != end) { lst.conj_BANG(read_form(rdr)); } if (token == null) { throw new ParseError("expected '" + end + "', got EOF"); } rdr.next(); return(lst); }
static MalVal EVAL(MalVal orig_ast, Env env) { MalVal a0, a1, a2, res; MalList el; while (true) { //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return(eval_ast(orig_ast, env)); } // apply list MalVal expanded = macroexpand(orig_ast, env); if (!expanded.list_Q()) { return(eval_ast(expanded, env)); } MalList ast = (MalList)expanded; if (ast.size() == 0) { return(ast); } a0 = ast[0]; String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() : "__<*fn*>__"; switch (a0sym) { case "def!": a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); env.set((MalSymbol)a1, res); return(res); case "let*": a1 = ast[1]; a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for (int i = 0; i < ((MalList)a1).size(); i += 2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i + 1]; let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; break; case "quote": return(ast[1]); case "quasiquote": orig_ast = quasiquote(ast[1]); break; case "defmacro!": a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); ((MalFunc)res).setMacro(); env.set(((MalSymbol)a1), res); return(res); case "macroexpand": a1 = ast[1]; return(macroexpand(a1, env)); case "try*": try { return(EVAL(ast[1], env)); } catch (Exception e) { if (ast.size() > 2) { MalVal exc; a2 = ast[2]; MalVal a20 = ((MalList)a2)[0]; if (((MalSymbol)a20).getName() == "catch*") { if (e is Mal.types.MalException) { exc = ((Mal.types.MalException)e).getValue(); } else { exc = new MalString(e.Message); } return(EVAL(((MalList)a2)[2], new Env(env, ((MalList)a2).slice(1, 2), new MalList(exc)))); } } throw e; } case "do": eval_ast(ast.slice(1, ast.size() - 1), env); orig_ast = ast[ast.size() - 1]; break; case "if": a1 = ast[1]; MalVal cond = EVAL(a1, env); if (cond == Mal.types.Nil || cond == Mal.types.False) { // eval false slot form if (ast.size() > 3) { orig_ast = ast[3]; } else { return(Mal.types.Nil); } } else { // eval true slot form orig_ast = ast[2]; } break; case "fn*": MalList a1f = (MalList)ast[1]; MalVal a2f = ast[2]; Env cur_env = env; return(new MalFunc(a2f, env, a1f, args => EVAL(a2f, new Env(cur_env, a1f, args)))); default: el = (MalList)eval_ast(ast, env); var f = (MalFunc)el[0]; MalVal fnast = f.getAst(); if (fnast != null) { orig_ast = fnast; env = f.genEnv(el.rest()); } else { return(f.apply(el.rest())); } break; } } }
public static MalVal read_hash_map(Reader rdr) { MalList lst = (MalList)read_list(rdr, new MalList(), '{', '}'); return(new MalHashMap(lst)); }
static MalVal EVAL(MalVal orig_ast, Env env) { MalVal a0, a1, a2, res; MalList el; while (true) { //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); if (!orig_ast.list_Q()) { return(eval_ast(orig_ast, env)); } // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return(ast); } a0 = ast[0]; String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() : "__<*fn*>__"; switch (a0sym) { case "def!": a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); env.set((MalSymbol)a1, res); return(res); case "let*": a1 = ast[1]; a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for (int i = 0; i < ((MalList)a1).size(); i += 2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i + 1]; let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; break; case "quote": return(ast[1]); case "quasiquote": orig_ast = quasiquote(ast[1]); break; case "do": eval_ast(ast.slice(1, ast.size() - 1), env); orig_ast = ast[ast.size() - 1]; break; case "if": a1 = ast[1]; MalVal cond = EVAL(a1, env); if (cond == Mal.types.Nil || cond == Mal.types.False) { // eval false slot form if (ast.size() > 3) { orig_ast = ast[3]; } else { return(Mal.types.Nil); } } else { // eval true slot form orig_ast = ast[2]; } break; case "fn*": MalList a1f = (MalList)ast[1]; MalVal a2f = ast[2]; Env cur_env = env; return(new MalFunc(a2f, env, a1f, args => EVAL(a2f, new Env(cur_env, a1f, args)))); default: el = (MalList)eval_ast(ast, env); var f = (MalFunc)el[0]; MalVal fnast = f.getAst(); if (fnast != null) { orig_ast = fnast; env = f.genEnv(el.rest()); } else { return(f.apply(el.rest())); } break; } } }
// repl static void Main(string[] args) { var repl_env = new Mal.env.Env(null); Func <string, MalVal> RE = (string str) => EVAL(READ(str), repl_env); // core.cs: defined using C# foreach (var entry in core.ns) { repl_env.set(new MalSymbol(entry.Key), entry.Value); } repl_env.set(new MalSymbol("eval"), new MalFunc( a => EVAL(a[0], repl_env))); int fileIdx = 0; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; fileIdx = 1; } MalList _argv = new MalList(); for (int i = fileIdx + 1; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! not (fn* (a) (if a false true)))"); RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); if (args.Length > fileIdx) { RE("(load-file \"" + args[fileIdx] + "\")"); return; } // repl loop while (true) { string line; try { line = Mal.readline.Readline("user> "); if (line == null) { break; } if (line == "") { continue; } } catch (IOException e) { Console.WriteLine("IOException: " + e.Message); break; } try { Console.WriteLine(PRINT(RE(line))); } catch (Mal.types.MalContinue) { continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); Console.WriteLine(e.StackTrace); continue; } } }
// repl static void Main(string[] args) { var repl_env = new Mal.env.Env(null); Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env); // core.cs: defined using C# foreach (var entry in core.ns) { repl_env.set(new MalSymbol(entry.Key), entry.Value); } repl_env.set(new MalSymbol("eval"), new MalFunc( a => EVAL(a[0], repl_env))); int fileIdx = 1; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; fileIdx = 2; } MalList _argv = new MalList(); for (int i=fileIdx; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! not (fn* (a) (if a false true)))"); RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); if (args.Length > fileIdx) { RE("(load-file \"" + args[fileIdx] + "\")"); return; } // repl loop while (true) { string line; try { line = Mal.readline.Readline("user> "); if (line == null) { break; } if (line == "") { continue; } } catch (IOException e) { Console.WriteLine("IOException: " + e.Message); break; } try { Console.WriteLine(PRINT(RE(line))); } catch (Mal.types.MalContinue) { continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); Console.WriteLine(e.StackTrace); continue; } } }
public static string _pr_str_args(MalList args, String sep, bool print_readably) { return(join(args.getValue(), sep, print_readably)); }
// repl static void Main(string[] args) { var repl_env = new Mal.env.Env(null); Func <string, MalVal> RE = (string str) => EVAL(READ(str), repl_env); // core.cs: defined using C# foreach (var entry in core.ns) { repl_env.set(new MalSymbol(entry.Key), entry.Value); } repl_env.set(new MalSymbol("eval"), new MalFunc( a => EVAL(a[0], repl_env))); int fileIdx = 0; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; fileIdx = 1; } MalList _argv = new MalList(); for (int i = fileIdx + 1; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! not (fn* (a) (if a false true)))"); RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); if (args.Length > fileIdx) { RE("(load-file \"" + args[fileIdx] + "\")"); return; } // repl loop while (true) { string line; try { line = Mal.readline.Readline("user> "); if (line == null) { break; } if (line == "") { continue; } } catch (IOException e) { Console.WriteLine("IOException: " + e.Message); break; } try { Console.WriteLine(PRINT(RE(line))); } catch (Mal.types.MalContinue) { continue; } catch (Mal.types.MalException e) { Console.WriteLine("Error: " + printer._pr_str(e.getValue(), false)); continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); Console.WriteLine(e.StackTrace); continue; } } }
static MalVal EVAL(MalVal orig_ast, Env env) { MalVal a0, a1, a2, res; while (true) { MalVal dbgeval = env.get("DEBUG-EVAL"); if (dbgeval != null && dbgeval != Mal.types.Nil && dbgeval != Mal.types.False) { Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); } if (orig_ast is MalSymbol) { string key = ((MalSymbol)orig_ast).getName(); res = env.get(key); if (res == null) { throw new Mal.types.MalException("'" + key + "' not found"); } return(res); } else if (orig_ast is MalVector) { MalVector old_lst = (MalVector)orig_ast; MalVector new_lst = new MalVector(); foreach (MalVal mv in old_lst.getValue()) { new_lst.conj_BANG(EVAL(mv, env)); } return(new_lst); } else if (orig_ast is MalHashMap) { var new_dict = new Dictionary <string, MalVal>(); foreach (var entry in ((MalHashMap)orig_ast).getValue()) { new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env)); } return(new MalHashMap(new_dict)); } else if (!(orig_ast is MalList)) { return(orig_ast); } // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return(ast); } a0 = ast[0]; String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() : "__<*fn*>__"; switch (a0sym) { case "def!": a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); env.set((MalSymbol)a1, res); return(res); case "let*": a1 = ast[1]; a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for (int i = 0; i < ((MalList)a1).size(); i += 2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i + 1]; let_env.set(key, EVAL(val, let_env)); } orig_ast = a2; env = let_env; break; case "quote": return(ast[1]); case "quasiquote": orig_ast = quasiquote(ast[1]); break; case "do": foreach (MalVal mv in ast.slice(1, ast.size() - 1).getValue()) { EVAL(mv, env); } orig_ast = ast[ast.size() - 1]; break; case "if": a1 = ast[1]; MalVal cond = EVAL(a1, env); if (cond == Mal.types.Nil || cond == Mal.types.False) { // eval false slot form if (ast.size() > 3) { orig_ast = ast[3]; } else { return(Mal.types.Nil); } } else { // eval true slot form orig_ast = ast[2]; } break; case "fn*": MalList a1f = (MalList)ast[1]; MalVal a2f = ast[2]; Env cur_env = env; return(new MalFunc(a2f, env, a1f, args => EVAL(a2f, new Env(cur_env, a1f, args)))); default: MalFunc f = (MalFunc)EVAL(ast[0], env); MalList arguments = new MalList(); foreach (MalVal mv in ast.rest().getValue()) { arguments.conj_BANG(EVAL(mv, env)); } MalVal fnast = f.getAst(); if (fnast != null) { orig_ast = fnast; env = f.genEnv(arguments); } else { return(f.apply(arguments)); } break; } } }
public static MalVal read_list(Reader rdr, MalList lst, char start, char end) { string token = rdr.next(); if (token[0] != start) { throw new ParseError("expected '" + start + "'"); } while ((token = rdr.peek()) != null && token[0] != end) { lst.conj_BANG(read_form(rdr)); } if (token == null) { throw new ParseError("expected '" + end + "', got EOF"); } rdr.next(); return lst; }
// eval static MalVal EVAL(MalVal orig_ast, Env env) { MalVal a0, a1, a2, res; MalVal dbgeval = env.get("DEBUG-EVAL"); if (dbgeval != null && dbgeval != Mal.types.Nil && dbgeval != Mal.types.False) { Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); } if (orig_ast is MalSymbol) { string key = ((MalSymbol)orig_ast).getName(); res = env.get(key); if (res == null) { throw new Mal.types.MalException("'" + key + "' not found"); } return(res); } else if (orig_ast is MalVector) { MalVector old_lst = (MalVector)orig_ast; MalVector new_lst = new MalVector(); foreach (MalVal mv in old_lst.getValue()) { new_lst.conj_BANG(EVAL(mv, env)); } return(new_lst); } else if (orig_ast is MalHashMap) { var new_dict = new Dictionary <string, MalVal>(); foreach (var entry in ((MalHashMap)orig_ast).getValue()) { new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env)); } return(new MalHashMap(new_dict)); } else if (!(orig_ast is MalList)) { return(orig_ast); } // apply list MalList ast = (MalList)orig_ast; if (ast.size() == 0) { return(ast); } a0 = ast[0]; if (!(a0 is MalSymbol)) { throw new Mal.types.MalError("attempt to apply on non-symbol '" + Mal.printer._pr_str(a0, true) + "'"); } switch (((MalSymbol)a0).getName()) { case "def!": a1 = ast[1]; a2 = ast[2]; res = EVAL(a2, env); env.set((MalSymbol)a1, res); return(res); case "let*": a1 = ast[1]; a2 = ast[2]; MalSymbol key; MalVal val; Env let_env = new Env(env); for (int i = 0; i < ((MalList)a1).size(); i += 2) { key = (MalSymbol)((MalList)a1)[i]; val = ((MalList)a1)[i + 1]; let_env.set(key, EVAL(val, let_env)); } return(EVAL(a2, let_env)); default: MalFunc f = (MalFunc)EVAL(ast[0], env); MalList arguments = new MalList(); foreach (MalVal mv in ast.rest().getValue()) { arguments.conj_BANG(EVAL(mv, env)); } return(f.apply(arguments)); } }
// repl static void Main(string[] args) { var repl_env = new Mal.env.Env(null); Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env); // core.cs: defined using C# foreach (var entry in core.ns) { repl_env.set(new MalSymbol(entry.Key), entry.Value); } repl_env.set(new MalSymbol("eval"), new MalFunc( a => EVAL(a[0], repl_env))); int fileIdx = 0; if (args.Length > 0 && args[0] == "--raw") { Mal.readline.mode = Mal.readline.Mode.Raw; fileIdx = 1; } MalList _argv = new MalList(); for (int i=fileIdx+1; i < args.Length; i++) { _argv.conj_BANG(new MalString(args[i])); } repl_env.set(new MalSymbol("*ARGV*"), _argv); // core.mal: defined using the language itself RE("(def! *host-language* \"c#\")"); RE("(def! not (fn* (a) (if a false true)))"); RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); RE("(def! *gensym-counter* (atom 0))"); RE("(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))"); RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"); if (args.Length > fileIdx) { RE("(load-file \"" + args[fileIdx] + "\")"); return; } // repl loop RE("(println (str \"Mal [\" *host-language* \"]\"))"); while (true) { string line; try { line = Mal.readline.Readline("user> "); if (line == null) { break; } if (line == "") { continue; } } catch (IOException e) { Console.WriteLine("IOException: " + e.Message); break; } try { Console.WriteLine(PRINT(RE(line))); } catch (Mal.types.MalContinue) { continue; } catch (Mal.types.MalException e) { Console.WriteLine("Error: " + printer._pr_str(e.getValue(), false)); continue; } catch (Exception e) { Console.WriteLine("Error: " + e.Message); Console.WriteLine(e.StackTrace); continue; } } }
public static string _pr_str_args(MalList args, String sep, bool print_readably) { return join(args.getValue(), sep, print_readably); }