+objvar vars
+objvar vals
+
+: lookup-var-frame ( var frame -- val? bool )
+ 2dup frame-vars vars setobj
+ frame-vals vals setobj
+
+ begin
+ vars fetchobj nil objeq? false =
+ while
+ 2dup vars fetchobj car objeq? if
+ 2drop
+ vals fetchobj car true
+ exit
+ then
+
+ vars fetchobj cdr vars setobj
+ vals fetchobj cdr vals setobj
+ repeat
+
+ 2drop false
+;
+
+: lookup-var ( var env -- val )
+ begin
+ 2dup nil objeq? false =
+ while
+ 2over 2over first-frame
+ lookup-var-frame if
+ -rot 2drop -rot 2drop
+ exit
+ then
+
+ enclosing-env
+ repeat
+
+ 2drop
+ bold fg red ." Unbound variable " print ." . Aborting." reset-term cr
+ abort
+;
+
+\ ---- Read ----
+
+variable parse-idx
+variable stored-parse-idx
+create parse-str 161 allot
+variable parse-str-span
+
+create parse-idx-stack 10 allot
+variable parse-idx-sp
+parse-idx-stack parse-idx-sp !
+
+: push-parse-idx
+ parse-idx @ parse-idx-sp @ !
+ 1 parse-idx-sp +!
+;
+
+: pop-parse-idx
+ parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
+
+ 1 parse-idx-sp -!
+
+ parse-idx-sp @ @ parse-idx ! ;
+
+
+: append-newline
+ '\n' parse-str parse-str-span @ + !
+ 1 parse-str-span +! ;
+
+: empty-parse-str
+ 0 parse-str-span !
+ 0 parse-idx ! ;
+
+: getline
+ parse-str 160 expect cr
+ span @ parse-str-span !
+ append-newline
+ 0 parse-idx ! ;
+
+: inc-parse-idx
+ 1 parse-idx +! ;
+
+: dec-parse-idx
+ 1 parse-idx -! ;
+
+: charavailable? ( -- bool )
+ parse-str-span @ parse-idx @ > ;
+
+: nextchar ( -- char )
+ charavailable? false = if getline then
+ parse-str parse-idx @ + @ ;
+
+: whitespace? ( -- bool )
+ nextchar BL =
+ nextchar '\n' = or ;
+
+: eof? ( -- bool )
+ nextchar 4 = ;
+
+: delim? ( -- bool )
+ whitespace?