Step Notes:

- step0_repl
    - prompt, input, READ, EVAL, PRINT, output
    - readline module
        - display prompt, read line of input

- use native eval in EVAL if available

- libedit/GNU readline:
    - use existing lib, wrap shell call or implement
    - load history file on first call
    - add non-blank lines to history
    - append to history file

- step1_read_print
    - types module:
        - add boxed types if no language equivalent:
            - nil, true, false, symbol, integer, string, list
        - pr_str:
            - stringify boxed types to their Mal representations
            - list/array is recursive
    - reader module:
        - stateful reader object
            - alternative: mutate token list
        - tokenize (if regex available)
            - standard regex pattern: "/[\s,]*(~@|[\[\]{}()'`~^@]|\"(?:\\.|[^\\\"])*\"|;.*|[^\s\[\]{}('\"`,;)]*)/"
        - read_str
            - read_form(new Reader(tokenize(str)))
        - read_form
            - detect errors
            - call read_list or read_atom
        - read_list
            - read_form until ')'
            - return array (boxed)
        - read_atom
            - return scalar boxed type:
                - nil, true, false, symbol, integer, string
    - repl loop
        - catch errors, print them and continue
        - impls without exception handling will need to have a global
          variable with checks for it at the beginning of critical
          code sections

- vectors
    - Basically: two array types that retain their boxed types, can be
      challenging depending on the language (e.g. JS, PHP: no clean
      way to derive new array types).
    - types module:
        - add vector boxed type
            - derived from array if possible
        - pr_str:
            - vector is recursive
        - sequential?
    - reader module:
        - read_vector:
            - re-use read_list but with different constructor, delims

- hash-maps
    - reader module:
        - re-use read_list function and apply that using hash-map
          constructor
    - types module:
        - pr_str addition
        - hash-map, map?, assoc, dissoc, get, contains?, keys,
          vals (probably assoc! and dissoc! for internal)
    - eval_map: eval the keys and values of hash_maps
    - EVAL:
        - if hash_map, call eval_map on it

- step2_eval
    - types module:
        - first, rest, nth on list
    - eval_ast:
        - if symbol, return value of looking up in env
        - if list, eval each item, return new list
        - if vector support, eval each item, return new vector
        - if hash_map support, eval each value, return new hash_map
        - otherwise, just return unchanged ast
    - EVAL/apply:
        - if not a list, call eval_ast
        - otherwise, apply first item to eval_ast of (rest ast)
    - repl_env as simple one level assoc. array (or hash_map)
        - store function as hash_map value

- step3_env
    - types module:
        - Env type:
            - find, set, get (no binds/exprs in constructor yet)
        - may need function type if HashMap is strongly typed (e.g. Java)
    - EVAL/apply:
        - def! - mutate current environment
        - let* - create new environment with bindings
    - _ref sugar

- step4_if_fn_do
    - types module:
        - function type (closure)
        - add function printing to pr_str
        - add binds/exprs handling to Env constructor with variable arity
        - functions (exported via types_ns):
            - move arith operations here
            - comparison operations (including =)
            - prn, pr_str, = (recursive)
            - list, list?, count, empty?
    - EVAL:
        - do:
        - if:
        - fn*:
            - simple if language supports closures
            - otherwise needs a way of representing functions that can
              have associated metadata
    - define "not" using REP/RE


- metadata
    - 

- step5_tco
    - types module:
        - function type:
            - stores: func, exp, env, params
            - func is EVAL in native mal case, otherwise reference to
              platform function
            - if metadata support, then store exp, env, params as
              metadata
        - update function printer to show function types
    - EVAL:
        - while loop around whole thing
        - cases where we directly return result of EVAL, instead set
          ast and env to what would be put in the EVAL, then loop.
            - for apply case, set env to new Env based on properties
              on the function

- step6_file
    - add read-string, eval, slurp platform wrappers
    - define load-file function
    - if files on command line, use load-file to run

- step7_quote
    - add is_pair and quasiquote functions
        - rewrite ast using cons/concat functions
        - if vectors, use sequential? instead of list? in is_pair
    - EVAL:
        - add 'quote', 'quasiquote' cases
    - types module:
        - add cons and concat functions
    - reader module:
        - add reader macros to read_form for quote, unquote,
          splice-unquote and quasiquote

- step8_macros
    - types module:
        - add first, rest functions
    - add is_macro_call and macroexpand
        - recursively macroexpand lists
        - if applying a macro function, run it on the ast first before
          continuing
    - call macroexpand apply in EVAL
    - EVAL:
        - add 'defmacro!' and 'macroexpand'
        - store ismacro property on function metadata

- step9_interop

- stepA_more
    - types module:
        - throw function
        - apply, map functions: should not directly call EVAL, which
          requires the function object to be runnable
    - EVAL:
        - try*/catch*: for normal exceptions, extracts string
          otherwise extracts full value

- Extra defintions needed for self-hosting
    - types module:
        - symbol?, nil?, true?, false?, sequential? (if not already)
        - first, rest
    - define cond and or macros using REP/RE

- Other misc:
    - conj function

- atoms
    - reader module:
        - @a reader macro -> (deref a)
    - types module:
        - pr_str case
        - atom type, atom, atom?, deref, reset!, swap!

- metadata
    - types module:
        - support meta property on symbols, hash-maps, lists, vectors,
          functions, atoms
        - add with-meta, meta functions
    - reader module:
        - ^ reader macro reads ^meta obj -> (with-meta obj meta)
