Implement step 3
authorVasilij Schneidermann <v.schneidermann@gmail.com>
Sat, 30 Apr 2016 18:12:07 +0000 (20:12 +0200)
committerVasilij Schneidermann <v.schneidermann@gmail.com>
Sat, 30 Apr 2016 21:01:14 +0000 (23:01 +0200)
chuck/chuck.md
chuck/env.ck [new file with mode: 0644]
chuck/notes.md
chuck/step3_env.ck [new file with mode: 0644]
chuck/util/Util.ck [new file with mode: 0644]

index 5ec2e5a..a915f9e 100644 (file)
   - An alternative is defining an error object (which belongs to the
     same supertype as the other legal return values) and checking its
     type by inspecting the user-tracked type field
+- No function pointers/functors/closures
+  - This is a bit unexpected as if you leave away the parentheses
+    holding the argument list and debug print a function, you'll see
+    it being recognized as a function, yet you can't store it anywhere
+    for passing it around
+  - So you get to implement functors and closures yourself...
+  - A functor is a class with a call method taking an argument list
+    and executing the code of the function you intend to pass around
+  - To use it, store an instance of its class somewhere, then use its
+    call method with an argument list
 - Other oddities
   - strict distinction between assigning values and references with
     two separate operators for them (`<<` for array append doesn't
@@ -78,3 +88,7 @@
     "" for `string` is)
   - If you abuse the type system too much, chances are you get a
     segfault or assert instead of an exception...
+  - Debug print shows the object and its type if you pass one
+    argument, if you pass more than one, it prints the concatenation
+    of their representations instead, so it's a bit hard to make out
+    what is a debug print and what isn't
diff --git a/chuck/env.ck b/chuck/env.ck
new file mode 100644 (file)
index 0000000..53d58c5
--- /dev/null
@@ -0,0 +1,54 @@
+public class Env extends MalObject
+{
+    MalObject outer; // this would ideally be Env, but isn't supported
+    MalObject data[0];
+
+    fun void init(MalObject env)
+    {
+        env @=> outer;
+    }
+
+    fun static Env create(MalObject env)
+    {
+        Env e;
+        e.init(env);
+        return e;
+    }
+
+    fun void set(string key, MalObject value)
+    {
+        value @=> data[key];
+    }
+
+    fun MalObject find(string key)
+    {
+        data[key] @=> MalObject value;
+
+        if( value != null )
+        {
+            return value;
+        }
+        else if( outer != null )
+        {
+            return (outer$Env).find(key);
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    fun MalObject get(string key)
+    {
+        find(key) @=> MalObject value;
+
+        if( value != null )
+        {
+            return value;
+        }
+        else
+        {
+            return MalError.create(Status.SYMBOL_NOT_FOUND, key);
+        }
+    }
+}
index e77c797..22e7444 100644 (file)
   my error handling to allow for format strings?
 - It would be worth a mention that you should extend the printer to
   handle "native" functions (or in oldtimey terms, subrs)
+
+# Step 3
+
+- You should modify both eval_ast *and* EVAL
+- Suggest the trick with destructuring the AST into `a0`, `a1`,
+  etc. variables for easier access.  Perhaps this can be used to clear
+  up the general language used with AST manipulation (like, first
+  parameter and second list element)?
+- What does def! return?  Emacs Lisp for instance returns the symbol
+  whereas the tests suggest the value should be returned instead...
diff --git a/chuck/step3_env.ck b/chuck/step3_env.ck
new file mode 100644 (file)
index 0000000..7922239
--- /dev/null
@@ -0,0 +1,194 @@
+// @import types/boxed/*.ck
+// @import types/MalObject.ck
+// @import types/mal/*.ck
+// @import types/MalSubr.ck
+// @import types/subr/*.ck
+// @import util/*.ck
+// @import reader.ck
+// @import printer.ck
+// @import env.ck
+
+fun MalObject READ(string input)
+{
+    return Reader.read_str(input);
+}
+
+fun MalObject EVAL(MalObject m, Env env)
+{
+    if( m.type == "list" )
+    {
+        if( (m$MalList).value().size() == 0 )
+        {
+            return m;
+        }
+
+        (m$MalList).value() @=> MalObject ast[];
+        (ast[0]$MalSymbol).value() => string a0;
+
+        if( a0 == "def!" )
+        {
+            (ast[1]$MalSymbol).value() => string a1;
+
+            EVAL(ast[2], env) @=> MalObject value;
+            if( value.type == "error" )
+            {
+                return value;
+            }
+
+            env.set(a1, value);
+            return value;
+        }
+        else if( a0 == "let*" )
+        {
+            Env.create(env) @=> Env let_env;
+            Util.sequenceToMalObjectArray(ast[1]) @=> MalObject bindings[];
+
+            for( 0 => int i; i < bindings.size(); 2 +=> i)
+            {
+                (bindings[i]$MalSymbol).value() => string symbol;
+                EVAL(bindings[i+1], let_env) @=> MalObject value;
+
+                if( value.type == "error" )
+                {
+                    return value;
+                }
+
+                let_env.set(symbol, value);
+            }
+
+            return EVAL(ast[2], let_env);
+        }
+
+        eval_ast(m, env) @=> MalObject result;
+        if( result.type == "error" )
+        {
+            return result;
+        }
+
+        (result$MalList).value() @=> MalObject values[];
+        values[0]$MalSubr @=> MalSubr subr;
+        MalObject.slice(values, 1) @=> MalObject args[];
+
+        return subr.call(args);
+    }
+    else
+    {
+        eval_ast(m, env) @=> MalObject result;
+        return result;
+    }
+}
+
+fun MalObject eval_ast(MalObject m, Env env)
+{
+    m.type => string type;
+
+    if( type == "symbol" )
+    {
+        (m$MalSymbol).value() => string symbol;
+        return env.get(symbol);
+    }
+    else if( type == "list" || type == "vector" || type == "hashmap" )
+    {
+        (m$MalList).value() @=> MalObject values[];
+        MalObject results[values.size()];
+
+        if( type != "hashmap" )
+        {
+            for( 0 => int i; i < values.size(); i++ )
+            {
+                EVAL(values[i], env) @=> MalObject result;
+
+                if( result.type == "error" )
+                {
+                    return result;
+                }
+
+                result @=> results[i];
+            }
+        }
+        else
+        {
+            for( 0 => int i; i < values.size(); i++ )
+            {
+                if( i % 2 == 0 )
+                {
+                    values[i] @=> results[i];
+                }
+                else
+                {
+                    EVAL(values[i], env) @=> results[i];
+                }
+            }
+        }
+
+        if( type == "list" )
+        {
+            return MalList.create(results);
+        }
+        else if( type == "vector" )
+        {
+            return MalVector.create(results);
+        }
+        else if( type == "hashmap" )
+        {
+            return MalHashMap.create(results);
+        }
+    }
+    else
+    {
+        return m;
+    }
+}
+
+fun string PRINT(MalObject m)
+{
+    return Printer.pr_str(m, true);
+}
+
+Env.create(null) @=> Env repl_env;
+repl_env.set("+", new MalAdd);
+repl_env.set("-", new MalSub);
+repl_env.set("*", new MalMul);
+repl_env.set("/", new MalDiv);
+
+fun string rep(string input)
+{
+    READ(input) @=> MalObject m;
+
+    if( m.type == "error" )
+    {
+        return Status.toMessage(m$MalError);
+    }
+
+    EVAL(m, repl_env) @=> MalObject result;
+    if( result.type == "error" )
+    {
+        return Status.toMessage(result$MalError);
+    }
+
+    return PRINT(result);
+}
+
+fun void main()
+{
+    ConsoleInput stdin;
+    string input;
+
+    while( true )
+    {
+        stdin.prompt("user>") => now;
+        stdin.getLine() => input;
+        rep(input) => string output;
+
+        if( output == "empty input" )
+        {
+            // proceed immediately with prompt
+        }
+        else
+        {
+            chout <= output + "\n";
+        }
+    }
+}
+
+main();
diff --git a/chuck/util/Util.ck b/chuck/util/Util.ck
new file mode 100644 (file)
index 0000000..041a8d3
--- /dev/null
@@ -0,0 +1,14 @@
+public class Util
+{
+    fun static MalObject[] sequenceToMalObjectArray(MalObject m)
+    {
+        if( m.type == "list" )
+        {
+            return (m$MalList).value();
+        }
+        else if( m.type == "vector" )
+        {
+            return (m$MalVector).value();
+        }
+    }
+}