factor: step 9
authorJordan Lewis <jordan@knewton.com>
Wed, 1 Apr 2015 19:36:33 +0000 (15:36 -0400)
committerJordan Lewis <jordan@knewton.com>
Wed, 1 Apr 2015 23:18:20 +0000 (19:18 -0400)
factor/src/core/core.factor
factor/src/malenv/malenv.factor
factor/src/printer/printer.factor
factor/src/step9_try/.step8_macros.factor.swp [new file with mode: 0644]
factor/src/step9_try/deploy.factor [new file with mode: 0644]
factor/src/step9_try/step9_try.factor [new file with mode: 0755]

index ae806b9..5b3ee22 100644 (file)
@@ -1,9 +1,12 @@
 ! Copyright (C) 2015 Jordan Lewis.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: kernel math sequences arrays lists printer locals io strings malenv reader io.files io.encodings.utf8 ;
+USING: kernel math sequences arrays lists printer locals io strings malenv reader io.files io.encodings.utf8
+       fry types combinators.short-circuit vectors hashtables assocs hash-sets sets grouping namespaces accessors ;
 
 IN: core
 
+SYMBOL: mal-apply
+
 :: pr-str-stack ( exprs readably? glue -- str )
     exprs [ readably? (pr-str) ] map glue join ;
 
@@ -36,4 +39,25 @@ CONSTANT: ns H{ { "+" [ first2 + ] }
                 { "nth" [ first2 swap nth ] }
                 { "first" [ first dup empty? [ drop nil ] [ first ] if ] }
                 { "rest" [ first dup empty? [ drop { } ] [ rest to-array ] if ] }
+                { "throw" [ first throw ] }
+                { "apply" [ unclip [ unclip-last append ] dip mal-apply get call( args fn -- maltype ) ] }
+                { "map" [ first2 swap '[ 1array _ mal-apply get call( args fn -- maltype ) ] map to-array ] }
+                { "nil?" [ first nil? ] }
+                { "true?" [ first t = ] }
+                { "false?" [ first f = ] }
+                { "symbol" [ first <malsymbol> ] }
+                { "symbol?" [ first malsymbol? ] }
+                { "keyword" [ first dup 1 head "\u00029e" = [ "\u00029e" prepend ] unless ] }
+                { "keyword?" [ first { [ string? ] [ 1 head "\u00029e" = ] } 1&& ] }
+                { "vector" [ >vector ] }
+                { "vector?" [ first vector? ] }
+                { "hash-map" [ 2 group parse-hashtable ] }
+                { "map?" [ first hashtable? ] }
+                { "assoc" [ unclip swap 2 group parse-hashtable assoc-union ] }
+                { "dissoc" [ unclip swap >hash-set '[ drop _ in? not ] assoc-filter ] }
+                { "get" [ first2 swap dup nil? [ nip ] [ ?at [ drop nil ] unless ] if ] }
+                { "contains?" [ first2 swap dup nil? [ nip ] [ ?at nip ] if ] }
+                { "keys" [ first keys ] }
+                { "vals" [ first values ] }
+                { "sequential?" [ first { [ vector? ] [ array? ] } 1|| ] }
              }
index 42071d6..a409fce 100644 (file)
@@ -39,4 +39,4 @@ M: malenv clear-assoc ( assoc -- )
     data>> clear-assoc ;
 
 : get-or-throw ( key assoc -- value )
-    ?at [ dup name>> "no variable " prepend throw ] unless ;
+    ?at [ dup name>> "'" dup surround " not found" append throw ] unless ;
index 4b8046b..a29d042 100644 (file)
@@ -21,7 +21,7 @@ IN: printer
         { [ dup array? ]     [ [ readably? (pr-str) ] map " " join "(" ")" surround ] }
         { [ dup vector? ]    [ [ readably? (pr-str) ] map " " join "[" "]" surround ] }
         { [ dup hashtable? ] [ unzip
-                               [ [ readably? (pr-str) ] bi@ " " glue ] [ " " glue ] 2map-reduce
+                               [ [ readably? (pr-str) ] bi@ " " glue ] 2map " " join
                                "{" "}" surround ] }
         { [ dup fn? ]        [ drop "#<fn>" ] }
         { [ dup t = ]        [ drop "true" ] }
diff --git a/factor/src/step9_try/.step8_macros.factor.swp b/factor/src/step9_try/.step8_macros.factor.swp
new file mode 100644 (file)
index 0000000..4be0397
Binary files /dev/null and b/factor/src/step9_try/.step8_macros.factor.swp differ
diff --git a/factor/src/step9_try/deploy.factor b/factor/src/step9_try/deploy.factor
new file mode 100644 (file)
index 0000000..03f485b
--- /dev/null
@@ -0,0 +1,16 @@
+USING: tools.deploy.config ;
+H{
+    { deploy-c-types? f }
+    { deploy-help? f }
+    { deploy-name "step9_try" }
+    { "stop-after-last-window?" t }
+    { deploy-unicode? f }
+    { deploy-console? t }
+    { deploy-io 3 }
+    { deploy-reflection 1 }
+    { deploy-ui? f }
+    { deploy-word-defs? f }
+    { deploy-threads? t }
+    { deploy-math? t }
+    { deploy-word-props? f }
+}
diff --git a/factor/src/step9_try/step9_try.factor b/factor/src/step9_try/step9_try.factor
new file mode 100755 (executable)
index 0000000..cfa3d21
--- /dev/null
@@ -0,0 +1,146 @@
+! Copyright (C) 2015 Jordan Lewis.
+! See http://factorcode.org/license.txt for BSD license.
+USING: io readline kernel system reader printer continuations arrays locals assocs sequences
+       combinators accessors fry quotations math malenv namespaces grouping hashtables lists
+       types core command-line combinators.short-circuit splitting ;
+
+IN: step9_try
+
+SYMBOL: repl-env
+
+DEFER: EVAL
+
+: eval-ast ( ast env -- ast )
+    {
+        { [ over malsymbol? ] [ get-or-throw ] }
+        { [ over sequence? ]  [ '[ _ EVAL ] map ] }
+        { [ over assoc? ]     [ '[ [ _ EVAL ] bi@ ] assoc-map ] }
+        [ drop ]
+    } cond ;
+
+:: eval-def! ( key value env -- maltype )
+    value env EVAL [ key env set-at ] keep ;
+
+:: eval-defmacro! ( key value env -- maltype )
+    value env EVAL t >>is-macro [ key env set-at ] keep ;
+
+:: eval-let* ( bindings body env -- maltype env )
+    body bindings 2 group env new-env
+    [| env pair | pair first2 env EVAL swap env ?set-at ]
+    reduce ;
+
+:: eval-do ( exprs env -- lastform env )
+    exprs empty?
+    [ { } f ]
+    [ exprs unclip-last env swap [ eval-ast ] dip nip env ]
+    if ;
+
+:: eval-if ( params env -- maltype env/f )
+    {
+        { [ params first env EVAL { f +nil+ } index not ] ! condition is true
+          [ params second env ] }
+        { [ params length 2 > ] [ params third env ] }
+        [ nil f ]
+    } cond ;
+
+:: eval-fn* ( params env -- maltype )
+    env params first [ name>> ] map params second <fn> ;
+
+:: eval-try* ( params env -- maltype )
+    [ params first env EVAL ]
+    [ params second second env new-env ?set-at params second third swap EVAL ]
+    recover ;
+
+: args-split ( bindlist -- bindlist restbinding/f )
+    [ "&" ] split dup length 1 >
+    [ first2 first ]
+    [ first f ]
+    if ;
+
+: make-bindings ( args bindlist restbinding/f -- bindingshash )
+    [ swap over length cut-slice [ zip ] dip ] dip
+    [ swap >array 2array suffix ]
+    [ drop ]
+    if*
+    >hashtable ;
+
+: apply ( args fn -- maltype newenv/f )
+    {
+        { [ dup fn? ]
+          [ [ exprs>> nip ] [ env>> nip ] [ binds>> args-split make-bindings ] 2tri <malenv> ] }
+        { [ dup callable? ] [ call( x -- y ) f ] }
+        [ drop "not a fn" throw ]
+    } cond ;
+
+: is-pair? ( maltype -- bool )
+    { [ sequence? ] [ empty? not ] } 1&& ;
+
+: quasiquote ( maltype -- maltype )
+    {
+        { [ dup is-pair? not ] [ [ "quote" <malsymbol> ] dip 2array ] }
+        { [ "unquote" over first symeq? ] [ second ] }
+        { [ dup first { [ is-pair? ] [ first "splice-unquote" swap symeq? ] } 1&& ]
+          [ [ "concat" <malsymbol> ] dip unclip second swap quasiquote 3array ] }
+        [ "cons" <malsymbol> swap unclip swap [ quasiquote ] bi@ 3array ]
+    } cond ;
+
+:: is-macro-call ( maltype env -- bool )
+    maltype { [ array? ]
+              [ first malsymbol? ]
+              [ first env at { [ fn? ] [ is-macro>> ] } 1&& ]
+            } 1&& ;
+
+: macro-expand ( maltype env -- maltype )
+    [ 2dup is-macro-call ]
+    [ [ unclip ] dip get-or-throw apply [ EVAL ] keep ]
+    while dop ;
+
+: READ ( str -- maltype ) read-str ;
+: EVAL ( maltype env -- maltype )
+    [ dup ]
+    [ over array?
+      [ [ macro-expand ] keep
+        over array?
+        [ [ unclip ] dip swap ! rest env first
+          {
+              { [ "def!" over symeq? ]  [ drop [ first2 ] dip eval-def! f ] }
+              { [ "defmacro!" over symeq? ] [ drop [ first2 ] dip eval-defmacro! f ] }
+              { [ "let*" over symeq? ]  [ drop [ first2 ] dip eval-let* ] }
+              { [ "do" over symeq? ]    [ drop eval-do ] }
+              { [ "if" over symeq? ]    [ drop eval-if ] }
+              { [ "fn*" over symeq? ]   [ drop eval-fn* f ] }
+              { [ "quote" over symeq? ] [ 2drop first f ] }
+              { [ "quasiquote" over symeq? ] [ drop [ first quasiquote ] dip ] }
+              { [ "macroexpand" over symeq? ] [ drop [ first ] dip macro-expand f ] }
+              { [ "try*" over symeq? ] [ drop eval-try* f ] }
+              [ swap [ prefix ] dip '[ _ EVAL ] map unclip apply ]
+          } cond ]
+        [ drop f ]
+        if ]
+      [ eval-ast f ]
+      if ]
+    while drop ;
+
+[ apply [ EVAL ] when* ] mal-apply set-global
+
+: PRINT ( maltype -- str ) pr-str ;
+: rep ( x -- x ) [ READ repl-env get EVAL PRINT ] [ nip pr-str ] recover ;
+
+: main-loop ( -- )
+            [ 1 ]
+            [ "user> " readline
+              [ 0 exit ] unless*
+              rep print flush ]
+            while ;
+
+f ns <malenv> repl-env set-global
+
+[ first repl-env get EVAL ] "eval" repl-env get data>> set-at
+command-line get "*ARGV*" repl-env get data>> set-at
+
+"(def! not (fn* (a) (if a false true)))" rep drop
+"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" rep drop
+"(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)))))))" rep drop
+"(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))))))))" rep drop
+
+MAIN: main-loop