// Return the value of a target symbol in this or an outer scope. public JKLVal Get(JKLSym keySymbol) { Env e = Find(keySymbol); if (e == null) { switch (keySymbol.ToString(true)) { // Some symbols are only valid when used in a particular context. case "unquote": throw new JKLEvalError("'unquote' used incorrectly (missing macro?)"); case "quasiquote": throw new JKLEvalError("'quasiquote' used incorrectly (missing macro?)"); case "splice-unquote": throw new JKLEvalError("'splice-unquote' used incorrectly (missing macro?)"); } //// If here the symbol simply hasn't been defined or is mis-spelt. throw new JKLLookupError("Get - Symbol not found '" + keySymbol.ToString(true) + "'"); } else { if (e.data.TryGetValue(keySymbol.getName(), out JKLVal value)) { return(value); } else { throw new JKLInternalError("Get - Find successful but symbol retrieval failed '" + keySymbol.ToString(true) + "'"); } } }
public void Set(JKLSym keySymbol, JKLVal value) { // Takes a symbol key and a JKL value and adds them to the environment. if (!data.TryAdd(keySymbol.getName(), value)) { // Symbol can shadow an equivalent in an outer scope but cannot be duped. throw new JKLEvalError("Attempt to redefine '" + keySymbol.ToString(true) + "'"); } }
// Setup the REPL. public REPL() { // Load built-in functions into the initial eval environment. try { myEnv = new Env(null); // Load core functions. foreach (var thing in JKLNameSpace) { JKLSym mSym = new JKLSym(thing.Key); if (myEnv.Find(mSym) == null) { myEnv.Set(mSym, thing.Value); } else { // The namespace itself may already have thrown an error but if not. throw new JKLInternalError("Attempt to refine symbol: '" + mSym.ToString(true) + "' with '" + thing.Value.ToString(true) + "'"); } } // Load the special eval core function. We have to do it here to access the REPL env. myEnv.Set(new JKLSym("eval"), new JKLFunc(args => { return(EVAL(args[0], myEnv)); })); //// EVAL(READ("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"))\")))))"), myEnv); //myEnv.Set(new JKLSym("load-file"), new JKLFunc(args => //{ // if (args[0] is JKLString mStr) // { // try // { // string FileText = File.ReadAllText(mStr.unbox()); // // Ee have the text of the file. Now build a file loader to eval. // StringBuilder sb = new StringBuilder(); // sb.Append("(do "); // sb.Append(FileText); // sb.Append(") "); // return EVAL(read_str(sb.ToString()), myEnv); // } // catch (Exception e) // { // throw new JKLEvalError("slurp: " + e.Message); // } // } // throw new JKLEvalError("load-file: expected filename but got '" + args[0] + "'"); //})); // Add 'core' functions defined using JKL itself. EVAL(READ("(def! *ARGV* (list))"), myEnv); EVAL(READ("(def! not (fn* (a) (if a false true)))"), myEnv); // Establish a gensym mechanism EVAL(READ("(def! *gensym-counter* (atom 0))"), myEnv); EVAL(READ("(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))"), myEnv); // This differs from the reference in that it uses a JKL-specific function slurp-do to // avoid the string quoting problem that can occur when trying to add a (do ... ) form // around the text retuned by raw slurp. EVAL(READ("(def! load-file (fn* (f) (eval (read-string (slurp-do f)))))"), myEnv); EVAL(READ("(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)))))))"), myEnv); EVAL(READ("(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)))))))))"), myEnv); } catch (System.TypeInitializationException e) { // Typically happens if there is a duplicate symbol in the namespace list Console.WriteLine("Unrecoverable error: " + e.InnerException.Message); } catch (JKLLookupError e) { Console.WriteLine(e.Message); } catch (JKLEvalError e) { Console.WriteLine(e.Message); } catch (JKLParseError e) { Console.WriteLine(e.Message); } }