   { parse-result-remaining parse-result-ast }
   get-slots 1vector  <parse-result> ;
 
-M: repeat0-parser parse ( state parser -- result )
+M: repeat0-parser (parse) ( state parser -- result )
      repeat0-parser-p1 2dup parse [ 
        nipd clone-result (repeat-parser) 
      ] [ 


TUPLE: action-parser p1 quot ;

M: action-parser parse ( state parser -- result )
   tuck action-parser-p1 parse dup [ 
     dup parse-result-ast rot action-parser-quot call
     swap [ set-parse-result-ast ] keep

: init-parser ( parser -- parser )
  get-next-id parser construct-boa over set-delegate ;

: from ( slice-or-string -- index )
  dup slice? [ slice-from ] [ drop 0 ] if ;

: get-cached ( input parser -- result )
  [ from ] dip parser-id packrat-cache get at at ;

: put-cached ( result input parser -- )
  parser-id dup packrat-cache get at [ 
    nip
  ] [ 
    H{ } clone dup >r swap packrat-cache get set-at r>
  ] if* 
  [ from ] dip set-at ;

PRIVATE>

: parse ( input parser -- result )
  packrat-cache get [
    2dup get-cached [
      [ (parse) dup ] 2keep put-cached
    ] unless* 
  ] [
    (parse)
  ] if ;

: packrat-parse ( input parser -- result )
  H{ } clone packrat-cache [ parse ] with-variable ;

<PRIVATE

TUPLE: token-parser symbol ;

M: token-parser (parse) ( input parser -- result )
  token-parser-symbol 2dup head? [
    dup >r length tail-slice r> <parse-result>
  ] [
    2drop f
  ] if ;
   
TUPLE: satisfy-parser quot ;

M: satisfy-parser (parse) ( state parser -- result )
  over empty? [
    2drop f 
  ] [
