aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--forth/core.fs3
-rw-r--r--forth/printer.fs41
-rw-r--r--forth/reader.fs119
-rw-r--r--forth/step6_file.fs259
-rw-r--r--forth/types.fs6
5 files changed, 329 insertions, 99 deletions
diff --git a/forth/core.fs b/forth/core.fs
index 4982a0e..71f43ca 100644
--- a/forth/core.fs
+++ b/forth/core.fs
@@ -74,3 +74,6 @@ defcore str ( argv argc )
loop
MalString. nip
endif ;;
+
+defcore read-string drop @ unpack-str read-str ;;
+defcore slurp drop @ unpack-str slurp-file MalString. ;;
diff --git a/forth/printer.fs b/forth/printer.fs
index d035e94..5309745 100644
--- a/forth/printer.fs
+++ b/forth/printer.fs
@@ -130,40 +130,17 @@ MalKeyword
kw unpack-keyword str-append ;;
drop
-: insert-\ ( str-addr str-len insert-idx -- str-addr str-len )
- -rot 0 str-append-char { addr len }
- dup dup addr + dup 1+ ( i i from to )
- rot len swap - cmove> ( i ) \ shift " etc to the right
- addr + [char] \ swap c! \ escape it!
- addr len
- ;
-
: escape-str { addr len }
s\" \"" str-append
- 0 ( i )
- begin
- dup len <
- while
- dup addr + c@ ( i char )
- dup [char] " = over [char] \ = or if ( i char )
- drop dup addr len rot insert-\ to len to addr
- 1+
- else
- dup 10 = if ( i ) \ newline?
- drop dup addr len rot insert-\ to len to addr
- dup addr + 1+ [char] n swap c!
- 1+
- else
- 13 = if ( i ) \ return?
- dup addr len rot insert-\ to len to addr
- dup addr + 1+ [char] r swap c!
- 1+
- endif
- endif
- endif
- 1+
- repeat
- drop addr len str-append
+ addr len + addr ?do
+ i c@ case
+ [char] " of s\" \\\"" str-append endof
+ [char] \ of s\" \\\\" str-append endof
+ 10 of s\" \\n" str-append endof
+ 13 of s\" \\r" str-append endof
+ -rot i 1 str-append rot
+ endcase
+ loop
s\" \"" str-append ;
MalString
diff --git a/forth/reader.fs b/forth/reader.fs
index 2ed3446..6547a79 100644
--- a/forth/reader.fs
+++ b/forth/reader.fs
@@ -1,8 +1,6 @@
require types.fs
require printer.fs
--2 constant skip-elem
-
\ Drop a char off the front of string by advancing the addr and
\ decrementing the length, and fetch next char
: adv-str ( str-addr str-len -- str-addr str-len char )
@@ -10,17 +8,6 @@ require printer.fs
dup 0= if 0 ( eof )
else over c@ endif ;
-: skip-spaces ( str-addr str-len char -- str-addr str-len non-space-char )
- begin
- dup bl = if
- -1
- else
- dup [char] , =
- endif
- while ( str-addr str-len space-char )
- drop adv-str
- repeat ;
-
: mal-digit? ( char -- flag )
dup [char] 9 <= if
[char] 0 >=
@@ -30,22 +17,32 @@ require printer.fs
: char-in-str? ( char str-addr str-len )
rot { needle }
- begin ( str-addr str-len )
- adv-str needle = if
- 2drop -1 -1 \ success! drop and exit
+ false -rot
+ over + swap ?do
+ i c@ needle = if drop true leave endif
+ loop ;
+
+: sym-char? ( char -- flag )
+ s\" \n\r\t\000[]{}()'\"`,; " char-in-str? 0= ;
+
+: skip-spaces ( str-addr str-len char -- str-addr str-len non-space-char )
+ begin
+ begin
+ dup s\" \n\r\t, " char-in-str?
+ while ( str-addr str-len space-char )
+ drop adv-str
+ repeat
+ dup [char] ; = if
+ drop
+ begin
+ adv-str s\" \n\r\000" char-in-str?
+ until
+ adv-str false
else
- dup 0= if
- 2drop 0 -1 \ str consumed, char not found.
- else
- 0 \ continue
- endif
+ true
endif
until ;
-s\" []{}()'\"`,; " constant non-sym-chars-len constant non-sym-chars
-: sym-char? ( char -- flag )
- non-sym-chars non-sym-chars-len char-in-str? 0= ;
-
defer read-form ( str-addr str-len -- str-addr str-len mal-obj )
: read-int ( str-addr str-len digit-char -- str-addr str-len non-digit-char mal-int )
@@ -56,13 +53,6 @@ defer read-form ( str-addr str-len -- str-addr str-len mal-obj )
until
int MalInt. ;
-: read-comment ( str-addr str-len sym-char -- str-addr str-len char skim-elem )
- drop
- begin
- adv-str = 10
- until
- adv-str skip-elem ;
-
: read-symbol-str ( str-addr str-len sym-char -- str-addr str-len char sym-addr sym-len )
new-str { sym-addr sym-len }
begin ( str-addr str-len sym-char )
@@ -106,8 +96,7 @@ defer read-form ( str-addr str-len -- str-addr str-len mal-obj )
read-form ,
repeat
drop adv-str
- old-here here>MalList
- ;
+ old-here here>MalList ;
: read-wrapped ( buf-addr buf-len quote-char sym-addr sym-len -- buf-addr buf-len char mal-list )
here { old-here }
@@ -116,40 +105,36 @@ defer read-form ( str-addr str-len -- str-addr str-len mal-obj )
old-here here>MalList ;
: read-form2 ( str-addr str-len char -- str-addr str-len char mal-obj )
- begin
- skip-spaces
- dup mal-digit? if read-int else
- dup [char] ( = if [char] ) read-list else
- dup [char] [ = if [char] ] read-list MalVector new tuck MalVector/list ! else
- dup [char] { = if [char] } read-list MalMap new tuck MalMap/list ! else
- dup [char] " = if read-string-literal else
- dup [char] ; = if read-comment else
- dup [char] : = if drop adv-str read-symbol-str MalKeyword. else
- dup [char] @ = if drop adv-str s" deref" read-wrapped else
- dup [char] ' = if drop adv-str s" quote" read-wrapped else
- dup [char] ` = if drop adv-str s" quasiquote" read-wrapped else
- dup [char] ~ = if
- drop adv-str
- dup [char] @ = if drop adv-str s" splice-unquote" read-wrapped
- else s" unquote" read-wrapped
- endif
- else
- dup [char] ^ = if
- drop adv-str
- read-form { meta } read-form { obj }
- meta mal-nil conj
- obj swap conj
- s" with-meta" MalSymbol. swap conj
+ skip-spaces
+ dup mal-digit? if read-int else
+ dup [char] ( = if [char] ) read-list else
+ dup [char] [ = if [char] ] read-list MalVector new tuck MalVector/list ! else
+ dup [char] { = if [char] } read-list MalMap new tuck MalMap/list ! else
+ dup [char] " = if read-string-literal else
+ dup [char] : = if drop adv-str read-symbol-str MalKeyword. else
+ dup [char] @ = if drop adv-str s" deref" read-wrapped else
+ dup [char] ' = if drop adv-str s" quote" read-wrapped else
+ dup [char] ` = if drop adv-str s" quasiquote" read-wrapped else
+ dup [char] ~ = if
+ drop adv-str
+ dup [char] @ = if drop adv-str s" splice-unquote" read-wrapped
+ else s" unquote" read-wrapped
+ endif
+ else
+ dup [char] ^ = if
+ drop adv-str
+ read-form { meta } read-form { obj }
+ meta mal-nil conj
+ obj swap conj
+ s" with-meta" MalSymbol. swap conj
+ else
+ read-symbol-str
+ 2dup s" true" str= if 2drop mal-true
+ else 2dup s" false" str= if 2drop mal-false
+ else 2dup s" nil" str= if 2drop mal-nil
else
- read-symbol-str
- 2dup s" true" str= if 2drop mal-true
- else 2dup s" false" str= if 2drop mal-false
- else 2dup s" nil" str= if 2drop mal-nil
- else
- MalSymbol.
- endif endif endif endif endif endif endif endif endif endif endif endif endif endif endif
- dup skip-elem =
- while drop repeat ;
+ MalSymbol.
+ endif endif endif endif endif endif endif endif endif endif endif endif endif endif ;
' read-form2 is read-form
: read-str ( str-addr str-len - mal-obj )
diff --git a/forth/step6_file.fs b/forth/step6_file.fs
new file mode 100644
index 0000000..d675f6e
--- /dev/null
+++ b/forth/step6_file.fs
@@ -0,0 +1,259 @@
+require reader.fs
+require printer.fs
+require core.fs
+
+core MalEnv. constant repl-env
+
+\ Fully evalutate any Mal object:
+def-protocol-method mal-eval ( env ast -- val )
+
+\ Invoke an object, given whole env and unevaluated argument forms:
+def-protocol-method invoke ( argv argc mal-fn -- ... )
+
+99999999 constant TCO-eval
+
+: read read-str ;
+: eval ( env obj )
+ begin
+ \ ." eval-> " dup pr-str safe-type cr
+ mal-eval
+ dup TCO-eval =
+ while
+ drop
+ repeat ;
+: print
+ \ ." Type: " dup mal-type @ type-name safe-type cr
+ pr-str ;
+
+MalDefault extend mal-eval nip ;; drop \ By default, evalutate to yourself
+
+MalKeyword
+ extend invoke { env list kw -- val }
+ 0 kw env list MalList/start @ cell+ @ eval get
+ ?dup 0= if
+ \ compute not-found value
+ list MalList/count @ 1 > if
+ env list MalList/start @ 2 cells + @ TCO-eval
+ else
+ mal-nil
+ endif
+ endif ;;
+drop
+
+\ eval all but the first item of list
+: eval-rest { env list -- argv argc }
+ list MalList/start @ cell+ { expr-start }
+ list MalList/count @ 1- { argc }
+ argc cells allocate throw { target }
+ argc 0 ?do
+ env expr-start i cells + @ eval
+ target i cells + !
+ loop
+ target argc ;
+
+MalNativeFn
+ extend invoke ( env list this -- list )
+ MalNativeFn/xt @ { xt }
+ eval-rest ( argv argc )
+ xt execute ( return-val ) ;;
+drop
+
+SpecialOp
+ extend invoke ( env list this -- list )
+ SpecialOp/xt @ execute ;;
+drop
+
+: install-special ( symbol xt )
+ SpecialOp. repl-env env/set ;
+
+: defspecial
+ parse-allot-name MalSymbol.
+ ['] install-special
+ :noname
+ ;
+
+defspecial quote ( env list -- form )
+ nip MalList/start @ cell+ @ ;;
+
+defspecial def! { env list -- val }
+ list MalList/start @ cell+ { arg0 }
+ arg0 @ ( key )
+ env arg0 cell+ @ eval dup { val } ( key val )
+ env env/set
+ val ;;
+
+defspecial let* { old-env list -- val }
+ old-env MalEnv. { env }
+ list MalList/start @ cell+ dup { arg0 }
+ @ to-list
+ dup MalList/start @ { bindings-start } ( list )
+ MalList/count @ 0 +do
+ bindings-start i cells + dup @ swap cell+ @ ( sym expr )
+ env swap eval
+ env env/set
+ 2 +loop
+ env arg0 cell+ @ TCO-eval
+ \ TODO: dec refcount of env
+ ;;
+
+defspecial do { env list -- val }
+ list MalList/start @ { start }
+ list MalList/count @ dup 1- { last } 1 ?do
+ env start i cells + @
+ i last = if
+ TCO-eval
+ else
+ eval drop
+ endif
+ loop ;;
+
+defspecial if { env list -- val }
+ list MalList/start @ cell+ { arg0 }
+ env arg0 @ eval ( test-val )
+ dup mal-false = if
+ drop -1
+ else
+ mal-nil =
+ endif
+ if
+ \ branch to false
+ list MalList/count @ 3 > if
+ env arg0 cell+ cell+ @ TCO-eval
+ else
+ mal-nil
+ endif
+ else
+ \ branch to true
+ env arg0 cell+ @ TCO-eval
+ endif ;;
+
+s" &" MalSymbol. constant &-sym
+
+MalUserFn
+ extend invoke { call-env list mal-fn -- list }
+ call-env list eval-rest { argv argc }
+
+ mal-fn MalUserFn/formal-args @ { f-args-list }
+ mal-fn MalUserFn/env @ MalEnv. { env }
+
+ f-args-list MalList/start @ { f-args }
+ f-args-list MalList/count @ ?dup 0= if else
+ \ pass nil for last arg, unless overridden below
+ 1- cells f-args + @ mal-nil env env/set
+ endif
+ argc 0 ?do
+ f-args i cells + @
+ dup &-sym m= if
+ drop
+ f-args i 1+ cells + @ ( more-args-symbol )
+ MalList new ( sym more-args )
+ argc i - dup { c } over MalList/count !
+ c cells allocate throw dup { start } over MalList/start !
+ argv i cells + start c cells cmove
+ env env/set
+ leave
+ endif
+ argv i cells + @
+ env env/set
+ loop
+
+ env mal-fn MalUserFn/body @ TCO-eval ;;
+drop
+
+defspecial fn* { env list -- val }
+ list MalList/start @ cell+ { arg0 }
+ MalUserFn new
+ env over MalUserFn/env !
+ arg0 @ to-list over MalUserFn/formal-args !
+ arg0 cell+ @ over MalUserFn/body ! ;;
+
+MalSymbol
+ extend mal-eval { env sym -- val }
+ 0 sym env get
+ dup 0= if
+ drop
+ ." Symbol '"
+ sym as-native safe-type
+ ." ' not found." cr
+ 1 throw
+ endif ;;
+drop
+
+: eval-ast { env list -- list }
+ here
+ list MalList/start @ { expr-start }
+ list MalList/count @ 0 ?do
+ env expr-start i cells + @ eval ,
+ loop
+ here>MalList ;
+
+MalList
+ extend mal-eval { env list -- val }
+ env list MalList/start @ @ eval
+ env list rot invoke ;;
+drop
+
+MalVector
+ extend mal-eval ( env vector -- vector )
+ MalVector/list @ eval-ast
+ MalVector new swap over MalVector/list ! ;;
+drop
+
+MalMap
+ extend mal-eval ( env map -- map )
+ MalMap/list @ eval-ast
+ MalMap new swap over MalMap/list ! ;;
+drop
+
+defcore eval ( argv argc )
+ drop @ repl-env swap eval ;;
+
+: rep ( str-addr str-len -- val )
+ read
+ repl-env swap eval
+ print ;
+
+: mk-args-list ( -- )
+ here
+ begin
+ next-arg 2dup 0 0 d<> while
+ MalString. ,
+ repeat
+ 2drop here>MalList ;
+
+s\" (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" rep drop
+
+create buff 128 allot
+77777777777 constant stack-leak-detect
+
+: repl ( -- )
+ begin
+ ." user> "
+ stack-leak-detect
+ buff 128 stdin read-line throw
+ while
+ buff swap
+ ['] rep
+ execute type
+ \ catch ?dup 0= if safe-type else ." Caught error " . endif
+ cr
+ stack-leak-detect <> if ." --stack leak--" cr endif
+ repeat ;
+
+: main ( -- )
+ mk-args-list { args-list }
+ args-list MalList/count @ 0= if
+ s" *ARGV*" MalSymbol. MalList/Empty repl-env env/set
+ repl
+ else
+ args-list MalList/start @ @ { filename }
+ s" *ARGV*" MalSymbol. args-list MalList/rest repl-env env/set
+
+ repl-env
+ here s" load-file" MalSymbol. , filename , here>MalList
+ eval print
+ endif ;
+
+main
+cr
+bye
diff --git a/forth/types.fs b/forth/types.fs
index bf159ad..5b8a211 100644
--- a/forth/types.fs
+++ b/forth/types.fs
@@ -309,6 +309,12 @@ drop
MalList new 0 over MalList/count ! constant MalList/Empty
+: MalList/rest { list -- list }
+ MalList new
+ list MalList/start @ cell+ over MalList/start !
+ list MalList/count @ 1- over MalList/count ! ;
+
+
MalType%
cell% field MalVector/list
deftype MalVector