Advertisement
Guest User

Command-line parser for CLI applications (FORTH)

a guest
Feb 1st, 2024
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.00 KB | Source Code | 0 0
  1. \ Command-line parser for CLI applications (FORTH)
  2. \ Public Domain. Use at your own risk.
  3. \ Revised: 2024-02-02
  4.  
  5. \ Usage:
  6. \ Create colon or :NONAME definitions for DEFERed words: SETOPT PARSEFN
  7. \ Feed your command string to PARSECMD
  8.  
  9. \ *** Related words you may need ***
  10.  
  11. \ CMDTAIL ( -- a u ) \ Return OS command-tail; tabs converted to blanks
  12. \ >BLANKS ( a u -- ) Convert ctrl chars to blanks in place
  13. \ -PATH ( a u -- a' u' ) Discard path spec from filename
  14. \ -EXT ( a u -- a' u' ) Discard filename extension and/or trailing '.'
  15. \ +EXT ( a1 u1 a2 u2 -- a3 u3 ) Conditionally append filename
  16. \ extension a2 u2 to filename string a1 u1 if no extension or
  17. \ trailing '.' was present and place in temporary buffer a3.
  18.  
  19. \ *** Needed words already present in the DX-Forth kernel ***
  20.  
  21. \ SKIP SCAN as commonly defined
  22.  
  23. : END postpone exit postpone then ; immediate
  24.  
  25. : BETWEEN 1+ swap within 0= ; \ better than 1+ WITHIN
  26. : 2NIP 2swap 2drop ;
  27. : NOOP ;
  28.  
  29. : /CHAR ( a u -- a2 u2 c ) 1 /string over 1- c@ ;
  30.  
  31. : /SIGN ( a u -- a' u' flag ) \ code def'n is simpler!
  32. dup if over c@ case [char] - of 1 /string -1 endof
  33. [char] + of 1 /string 0 endof 0 swap endcase exit then 0 ;
  34.  
  35. : /NUMBER ( a u -- a2 u2 d|ud ) \ N.B. empty string returns d=0
  36. /sign >r 0 0 2swap >number 2swap r> if dnegate then ;
  37.  
  38. \ *** Misc library words ***
  39.  
  40. \ Split string at character leaving first on top
  41. : SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
  42.  
  43. : .ABORT ( -- ) ." ... aborting" abort ;
  44.  
  45. \ *** Command-line parser ***
  46.  
  47. \ Error handling
  48. : .BAD ( -- ) cr ." Invalid item" .abort ;
  49. : ?BAD ( f -- ) 0= if .bad then ; \ empty or failed
  50. : ?END ( u -- 0 ) dup if .bad then ; \ expected end
  51.  
  52. \ Convert double number to single
  53. : ?D>S ( d|ud -- n|u ) -1 0 between ?bad ;
  54.  
  55. \ Range check single number
  56. : ?RNG ( n|u lo hi -- n|u ) 2>r dup 2r> between ?bad ;
  57.  
  58. \ Number parsers
  59. : /DNUM ( a u -- a' 0 d|ud )
  60. dup ?bad /number 2swap ?end 2swap ;
  61.  
  62. : /NUM ( a u -- a' 0 n|u ) /dnum ?d>s ;
  63.  
  64. : /HEX ( a u -- a' 0 n|u ) base @ >r hex /num r> base ! ;
  65.  
  66. create ARGC ( -- a ) 3 cells allot
  67.  
  68. \ Reset command parser
  69. : /ARG ( -- ) argc off ;
  70.  
  71. \ Assign string for parsing
  72. : !ARG ( a u -- ) argc cell+ 2! /arg ; here 0 !arg
  73.  
  74. \ Get next blank delimited arg
  75. : ARGV ( -- a u -1 | 0 )
  76. 1 argc +! argc cell+ 2@ 0 0
  77. argc @ 0 ?do 2drop bl skip bl split loop 2nip
  78. dup if -1 end and ;
  79.  
  80. \ Set switch
  81. defer SETOPT ( a u char -- a' u' ) ' drop is setopt
  82.  
  83. \ Parse filenames
  84. defer PARSEFN ( -- ) ' noop is parsefn
  85.  
  86. \ Parse switches
  87. : PARSEOPT ( -- )
  88. begin argv while ( not end )
  89. /char $FD and [char] - ( '-' or '/')
  90. - if 2drop -1 argc +! ( backup) end
  91. begin dup while /char setopt repeat 2drop
  92. repeat ;
  93.  
  94. \ Parse the command string
  95. : PARSECMD ( a u -- )
  96. !arg parseopt parsefn argv ?end drop ;
  97.  
  98. \ *** Additional words for comma-separated numbers ***
  99.  
  100. \ N.B. empty field returns n=0
  101. : /INT ( a u -- a' u' n|u ) /number ?d>s ;
  102.  
  103. defer (NUM) ' /int is (num)
  104.  
  105. \ Skip next char if not empty; abort if no match
  106. : ?SKIP ( a u char -- a' u' )
  107. over if >r /char r> = ?bad end drop ;
  108.  
  109. \ Parse first number; abort if string empty
  110. : FIRSTNUM ( a u -- a' u' num true )
  111. dup ?bad (num) -1 ;
  112.  
  113. \ Parse comma separated number; false if string empty
  114. : NEXTNUM ( a u -- a' u' num true | a 0 false )
  115. dup if [char] , ?skip firstnum end 0 ;
  116.  
  117.  
  118. 1 [if] \ test
  119.  
  120. : sete ( a u -- a' u' )
  121. firstnum if .
  122. nextnum if .
  123. nextnum if .
  124. then then then ?end ;
  125.  
  126. \ Set switch
  127. :noname ( a u char -- a' u' )
  128. case
  129. [char] a of ." a " endof
  130. [char] b of ." b " endof
  131. [char] c of ." c " endof
  132. [char] d of ." d " /num . endof
  133. [char] e of ." e " sete endof
  134. .bad
  135. endcase ; is setopt
  136.  
  137. \ Parse filenames
  138. :noname ( -- )
  139. cr begin argv while type space repeat ; is parsefn
  140.  
  141. s" -a -b -c -abc -d1 -e2 -e2,3,4 -e,5 filename1 filename2" cr parsecmd
  142.  
  143. \ a b c a b c d 1 e 2 e 2 3 4 e 0 5
  144. \ filename1 filename2 ok
  145.  
  146. [then]
  147.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement