   #! The latter indicates that it is the beginning of a
   #! new rule.
   [
-    [ 
-      'non-terminal' ,
-      'terminal' ,
-      'foreign' ,
-      'range-parser' ,
-      'any-character' ,
+    [
+      [ 
+        'non-terminal' ,
+        'terminal' ,
+        'foreign' ,
+        'range-parser' ,
+        'any-character' ,
+      ] choice* 
+      [ dup , "*" token hide , ] seq* [ first <ebnf-repeat0> ] action ,
+      [ dup , "+" token hide , ] seq* [ first <ebnf-repeat1> ] action ,
+      [ dup , "?[" token ensure-not , "?" token hide , ] seq* [ first <ebnf-optional> ] action ,
+      ,
     ] choice* ,
     [
       "=" syntax ensure-not ,
       "=>" syntax ensure ,
     ] choice* ,
   ] seq* [ first ] action ;
+
+DEFER: 'action'
 
 : 'element' ( -- parser )
   [

  ] choice* ;
  
: 'choice' ( -- parser )
  'actioned-sequence' sp "|" token sp list-of [ 
    dup length 1 = [ first ] [ <ebnf-choice> ] if
  ] action ;
 

GENERIC: build-locals ( code ast -- code )

M: ebnf-sequence build-locals ( code ast -- code )
  #! Note the need to filter out this ebnf items that
  #! leave nothing in the AST
  elements>> filter-hidden dup length 1 = [ 
    first build-locals 
  ]  [
    dup [ ebnf-var? ] filter empty? [
      drop 
    ] [ 
      [
        "USING: locals sequences ;  [let* | " %
          dup length swap [
            dup ebnf-var? [
              name>> % 
              " [ " % # " over nth ] " %
            ] [
              2drop
            ] if
          ] 2each
          " | " %
          %  
          " nip ]" %     
      ] "" make 
    ] if
  ] if ;

M: ebnf-var build-locals ( code ast -- )
