rebol [
	title:		"args - generic command-line argument parser."
	file: 		%args.r
	version:	0.1.10
	date: 		2008-12-17
	author:		"Maxim Olivier-Adlhoch"
	copyright:	"Copyright (c) 2008 Maxim Olivier-Adlhoch"
	license:    'mit
	purpose:	"End to end command-line argument management.  Using a command-line description dialect parse a command-line and if its valid, construct a context which reflects it."
	note:       "requires vprint.r module from rebol.org to be run prior to this one"

	;-- REBOL.ORG header --
	library: [
		level:          'intermediate
		platform:       'all
		type:           [ module ]
		domain:         [ external-library dialects shell  ]
		tested-under:   [win view 2.7.5]
		license:        'MIT
		see also:       "vprint.r args-test.r"
	]


	to-do: {
	-must continue expanding command-line error reporting
	-debug all datatypes when defining optional datatype for arguments (order and mutual inclusion)
	}

	changes: {}
	
	history: {
		v0.1.1 - 2008-04-09/16:50:42 (max)
			-first history entry, after lengthy dev cycle
			-added extended file path notation, to allow paths with spaces. (ex, program files, documents and settings, etc)

		v0.1.6 - 2008-12-10/02:11:32 (max)
			-added a bit more docs about -????? flag
			-started embedded documentation at end of script

		v0.1.7 - 2008-12-15/00:34:48 (max)
			-switch now can act as a toggle between two values, instead of only supporting boolean on/off functionality.
			-added '0' & '1' to the valid boolean notations in command parsing
			-converted to simple rebol 'DO format for rebol.org release

		v0.1.8 - 2008-12-16/04:18:11 (max)
			-added command-line error reporting basics should report missing arguments
			-explicit support for command-line reporting in decimal! and integer! datatypes.

		v0.1.9 - 2008-12-16/20:26:20 (max)
			-fixed command-line parsing bug when specifying integer! and decimal! as both valid for a type...

		v0.1.10 - 2008-12-17/03:31:14 (max)
			-flags now specifically enfore that flags follow string alphanumeric word format (starting with a "-")
	}
]


;; conditional lib execution, simulates C/C++ #ifndef 
do unless (value? 'lib-args) [[

;; declare lib
lib-args: true


lib-args-ctx: context [
	;- LIBS
	;slim/open 'core none ; for slim-link
	;os: slim/open 'os none
	
	;----------
	; GLOBALS
	;- max-args
	max-args: 1000 ; just a safety for parser deadlock or application hacking (too many args could cause some apps to crash)
	
	;- error-index-symbol: 
	error-index-symbol: ""
	
	;- load-error:
	; used for storage of error info when parsing command lines.
	load-error: none
	
	;- load-error-offset:
	load-error-offset: none
	
	;- error-msg:
	error-msg: none
	
	
	;- flag-name
	flag-name: none
	
								

	
	
	;----------
	; INTERNALS
	; localise values:
	rlv-str: val: none
	
	
		
	;-  
	;- VALUE-RULES[]
	value-rules: context [
		;-    context locals
		here-text: none
	
	
		;-    bitsets
		space: #" " ; newline and tab are already defined
		digit: charset [#"0" - #"9"]
		letter: charset [#"A" - #"Z"   #"a" - #"z" ]
		upper-case: charset [#"A" - #"Z"]
		alphanumeric: charset [ #"A" - #"Z"  #"a" - #"z"  #"0" - #"9"  #"-" #"_"]
		chars: complement charset [#"\" #"'"]
		block-end: complement charset [#"]"]
		anychar: complement charset [#" "]
		
		xtdpath-char: complement charset "'"
		
		white-space: charset reduce [ space tab newline]
		spaces: [some white-space]
		spacer: _: [any white-space] ; when its not required
		non-white-space: complement white-space
		separator: [[some white-space] | end]
	
	;-    type rules
		escape-char: [#"\" skip]
		string: [
			[
				; one of 3 string syntaxes!
				[{'} copy rlv-str [any [chars | escape-char]] {'}] |
				[{"} copy rlv-str [any [chars | escape-char]] {"}] |
				["{" copy rlv-str [any [chars | escape-char]] "}"]
			]
			
			( rlv-str: any [rlv-str copy ""] )
		
		] ; skip escaped chars{"} ]
		
		integer: [ 0 1 "-" some digit]
		float:   [ 0 1 "-" any digit "." some digit 0 1 ["E" some digit]]
		bool:  ["true" | "false" | "on" | "off" | "0" | "1"]
		flag: [ ["-" 0 1 "-" letter any alphanumeric] ] ; -switches cannot start with a digit
		datatype: [ [some letter "!"] ]
		path: file-path: [ 0 1 "%" ["./" | "/" | "\" | ".\" | [letter ":"]] any [non-white-space ]]
		block: [ "[" copy rlv-str to "]" skip] ; no "]" chars in any way within blocks.
		extended-file-path: [ 
			0 1 "%" 
			(rlv-str: none)
			[
				"'" copy rlv-str [ ["./" | "/" | "\" | ".\" | [letter ":"]]  any xtdpath-char]  "'"
			]|[
				copy rlv-str [["./" | "/" | "\" | ".\" | [letter ":"]] any [non-white-space ]]
			]
		]
		
		; any text (all chars until next flag (but not including it) )
		skip-text-rule: [skip here-text: ]
		skip-rule: none
		here-mem: none
		skip-flag: none
		any-text: [
			( skip-rule: skip-text-rule)
			some [
				here-mem:
				[
					some white-space skip-flag: flag  ( ; note that "-doo" in ski-doo will not match as a flag
						here-text: here-mem
						
						skip-rule: "_" ; we want the next skip to fail, and thus end the skipping, since here will start at a "-"
					)
					 :skip-flag
				]
				| 
				skip-rule 
			]
			:here-text 
		]
		
		error-at: none
		;-    compound rules
		value: [
			[
				error-at:
				copy val string  (append current-rblk rlv-str vprint ["STRING!: " rlv-str] rlv-str: none) | ; here we use rlv-str, because it contains no " chars
				copy val path    (if #"%" = first val [remove val] append current-rblk to-rebol-file val vprint ["PATH!: " val	]) |
				copy val bool    (append current-rblk to-logic do val vprint ["BOOL!: " val]) | 
				copy val float   (append current-rblk to-issue val vprint ["FLOAT!: " val]) | 
				copy val integer (append current-rblk to-issue val vprint ["INT!: " val]) |
				copy val flag  (append current-rblk to-word val vprint ["LABEL!: " val	]) |
				copy val datatype  (append current-rblk to-datatype val vprint ["DATATYPE!: " val	]) |
				copy val block   (append/only current-rblk val: parse/all rlv-str " " vprint ["BLOCK!: " mold/all val]) 
				(arg-load-count: arg-load-count + 1 if arg-load-count > max-args [print "arguments maximum count reached" halt])
				
			]
			(val: none)
		]
		
		; this rule loads ALL values from string, if they obey basic values rules	
		parameters: [ (current-rblk: copy [] arg-load-count: 0) some [ value | spaces]]
		
		arg-load-count: 0
		current-rblk: copy []
		
		
		;-    arg loading rules
		parsed-value: none
;		integer-rule: [
;			(parsed-value: none)
;			[
;				copy val integer 
;				(parsed-value: to-issue val vprint ["INT!: " val])
;				val: none
;			]|
;			[
;				(to-error "FUCK")
;			]
;		]
		
		integer-rule: [(parsed-value: none ) copy val integer separator   (parsed-value: to-issue val vprint ["INT!: " val]) val: none ]
		float-rule:   [(parsed-value: none ) copy val float  separator  (parsed-value: to-issue val vprint ["FLOAT!: " val]) val: none ]
		text-rule:    [(parsed-value: none) copy val any-text separator (parsed-value: trim val vprint ["TEXT!: " parsed-value]) val: none]
		bool-rule:    [(parsed-value: none) copy val bool separator (parsed-value: to-logic do val vprint ["BOOL!: " val] val: none)]
		string-rule:  [(parsed-value: none) string separator (parsed-value: rlv-str vprint ["STRING!: " rlv-str] rlv-str: none)] ; here we use rlv-str, because it contains no ' chars
		file-rule:    [(parsed-value: none) extended-file-path  separator (parsed-value: to-rebol-file rlv-str vprint ["PATH!: " parsed-value] val: none)] ; here we use rlv-str, because it contains no ' chars
	]
		
		
	;-    spec parsing rules
	;---
	; these are in fact words, not actual datatypes, since the spec is not loaded.
	;
	; CRITICAL NOTE!:
	;     Following list must be symmetric with switch list and untyped args in arg-ctx/init()
	;---
	spec-datatypes: ['integer! | 'string! | 'logic! | 'file! | 'decimal! | 'text! ]
		
	
;	
;	either LINKED?: false [
;		args: system/script/args
;	][
;		; just some generic argument
;		args: { -stack [fubar 001 25] plate ball 1 box counter  C:/some/path/to/a/file.txt +LR +BLUR }
;	]
	
	;-  
	;- FUNCTIONS
	;-------------
	;-     load-args()
	;-------------
	load-args: func [
		argstr [string!]
	][
		return either parse/all/case argstr value-rules/parameters [
			
			; all is well with command-line parsing, return loaded block
			first reduce [value-rules/current-rblk value-rules/current-rblk: rlv-str: val: none]
		
		][
			; an error occured, clear all data and return the error
			value-rules/current-rblk: rlv-str: val: none
			value-rules/error-at
		]
	]
	
	
	;-------------
	;-     construct-args()
	;-------------
	set 'construct-args func [
		"returns an object with all fields filled and fully validated, according to spec, an error string otherwise."
		args [string! block!] "either the argument string or a load-args block"
		spec [block!] {
		-flag [
			[switch!] arg type! [type! [...]] [default-value] ; both type and default value are optional
			[|] ; optional, changes flag's mode to mutually exclusive args based on type
			arg [...] 
			[|] ; as above
			arg [...]
		]
		-flag [...]

		
		; trailing args by type OR nothing, in which case any additional args are put in values: field
		
		integer! [default-value]
		file! [...]		
		...
}
		/local arg-ctx flag-name arg-name arg-type arg-value-when-set arg-value-when-unset arg-default 
			subrule flag-type here-error spec-error
			current-flag-rule new-flag-rule cmd-line-rule flag-ctx flags
			arg-val args-ctx error-msg entry-args
	][
		;---------------------
		;-         args-ctx:
		; used to store all arg names and default values, later on is converted to an object and filled up
		; *supplied-flags is used to know what flags where actually supplied on the command-line,
		;    in case some app features need this data (two flags sharing the same args).
		args-ctx: copy/deep [*supplied-flags: [] *error: none]
		
		;---------------------
		;-         flags:
		; we store all generated flag ctx here.
		; once spec parsing is done, we will run through this list and create a compiled command-line parse rule
		; and it will fill up the end-result args-ctx.
		flags: copy []
		
		;---------------------
		;-         cmd-line-rule:
		; this stores the compiled command-line parse rule, with all flags and args within.
		cmd-line-rule: copy []
		

		;---------------------
		; entry-args
		entry-args: args  ;  potentially deprecated ????
		
		
		;---------------------
		; perform parsing within a safe and verbose error recovery
		;
		if error? spec-error: try [
			;--------------------------------------------------------------------
			;---                                                              ---    
			;---                   DEFINE ARG SPEC CONTEXTS                   ---
			;---                                                              ---    
			;--------------------------------------------------------------------
			vprobe args
			vprint "^/----------------------^/starting args-context()"

			;-------------
			;-         flag-ctx[]
			; 
			; each flag in your command-line spec, will generate one flag-ctx and add it to the flags block.
			;-------------
			flag-ctx: context [
				;-             name:
				name: none
				;-             type:
				type: 'arg
				;-             args:
				args: none
				;-             rule:
				rule: none
				;-             required?:
				required?: false

				;------------------------------
				;-             apply-defaults()
				;------------------------------
				apply-defaults: func [/local arg][
					foreach arg args [
						arg/apply-default
					]
				]
				
				;------------------------------
				;-             link-args()
				;------------------------------
				; here we check if all supplied args are valid amongst themselves.
				;
				; possible errors:
				; 	-a required arg is specified AFTER optional ones
				;------------------------------
				link-args: func [
					/local required? arg rule-tmp paren
				][
					vin "link-args()"
					required?: true
					
					
					foreach arg args [
						;-----------------------------------------------------------------
						; -????? is a shadow tag which never gets linked in the system.
						; its purpose is to modify the args-ctx directly, without really
						; adding args to your spec.
						unless name = '-????? [
							; make sure required args are not specified after optional ones.
							rule-tmp: tail rule
							any [
								all [
									not required? 
									arg/required?
									(make error! rejoin ["ERROR: Required arg '" arg/name "' follows optional args in " name " flag spec!"])
								]
								all [
									not arg/required?
									required?: false
								]
							]
							
							append rule arg/rule
							new-line rule-tmp true
						]
						; add args and defaults to args-ctx
						append args-ctx to-set-word arg/name
						append args-ctx arg/default
					]
					if rule [
						paren: [vout] 
						append/only rule to-paren paren
					]
					vout
				]
				
				;------------------------------
				;-             register()
				;------------------------------
				register: func [][
					;-----------------------------------------------------------------
					; -????? is a shadow tag which never gets linked in the system.
					; its purpose is to modify the args-ctx directly, without really
					; adding args to your spec.
					unless name = '-????? [
						append flags self
					]
				]
				
				;-----------------
				;-             confirm-flag-name()
				;-----------------
				confirm-flag-name: func [
				][
					vin [{confirm-flag-name()}]
					unless parse/all to-string name value-rules/flag [
						to-error join "ARGS.r template dialect ERROR:  Invalid flag name: " to-string name
					]
					vout
				]
				
				

				;------------------------------
				;-             init()
				;----------
				; NOTE before calling init, ALL object fields MUST be filled-in
				;------------------------------
				init: func [
					/local paren
				][
					vin "init-flag()"
					
					confirm-flag-name
					
					;----------
					; support required? in flag spec
					;----------
					
					args: copy []
					
					
					
					
					
					;-----------------------------------------------------------------
					; -????? is a shadow tag which never gets linked in the system.
					; its purpose is to modify the args-ctx directly, without really
					; adding args to your spec.
					;
					; use it to set default values, without specifying any tag on the command line, for example
					unless name = '-????? [
					
						; the required? mode will be a post parse op, where we check if all required flags where specified at least
						; once in the *supplied-flags field
						append/only cmd-line-rule rule: compose/deep [(to-string name) value-rules/separator ]
						append cmd-line-rule '|

						paren: compose[vin flag-name: (to-string name)] 
						append/only rule to-paren paren 
						
						paren: compose [append args-ctx/*supplied-flags (to-lit-word name)]
						unless type = 'switch [
							append paren bind 'apply-defaults self
						]
						new-line/skip paren false 0
						paren: to paren! paren
						append/only rule paren


					]
					vout
				]
			]
			
			;-------------
			;-         arg-ctx[]
			;-------------
			arg-ctx: context [
				;-             name:
				name: none
				;-             type:
				type: 'arg
				;-             flag:
				flag: none
				;-             rule:
				rule: none
				;-             default:
				default: none
				;-             required?:
				required?: true
				;-             types:
				types: none
				
				;------------------------------
				;-             apply-default()
				;------------------------------
				apply-default: func [][
					set in args-ctx name default
				]
				
				;------------------------------
				;-             apply-arg()
				;------------------------------
				apply-arg: func [/using value][
					switch type [
						arg [
							set in args-ctx name value
						]
						switch [
							vprint "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
							set in args-ctx name self/value
							vprobe name
							vprobe args-ctx
						]
					]
				]
				
				;------------------------------
				;-             init()
				;------------------------------
				init: func [
					/local real-type datatype paren src-rule
				][
					vin "init-arg()"
					;----------
					; NOTE before calling init, object fields must be filled-in
					;----------
					
					;-----------------------------------------------------------------
					; -????? is a shadow tag which never gets linked in the system.
					; its purpose is to modify the args-ctx directly, without really
					; adding args to your spec.
					;
					; use it so set default values, without specifying any tag on the command line, for example
					unless name = '-????? [
						; we start by creating the type rule
						
						switch type [
							arg [
								either types [
									rule: copy []
									foreach datatype types [
										;---
										; NOTE: switch list must be symmetric with all allowed 
										; types below and in spec-parsing arg-rule
										src-rule: switch datatype [
											; the switch value is a word, the return value is a datatype parse block
											integer! [ 'integer-rule ]
											decimal! [ 'float-rule   ]
											logic! [ 'bool-rule]
											text! [ 'text-rule]
											string! [ 'string-rule]
											file! [ 'file-rule]
										]
										append/only rule get in value-rules src-rule
										append rule '|
									]
									remove back tail rule
								][
									;---
									; NOTE: This list must be symmetric with switch list above and in spec-parsing arg-rule
									rule:  [integer-rule | float-rule | bool-rule | text-rule | string-rule | file-rule]
									bind rule value-rules
								]
								
								src-rule: rule
								
								paren: compose [apply-arg/using value-rules/parsed-value]
								bind paren 'paren
								
								rule: compose/only [(rule) (to-paren paren )]
								;append/only rule value-rules/separator
								unless required? [
									rule: compose/only [0 1 (rule)]
								]
								vprobe mold/all rule
								
								;---------------------
								; add error handling to value-loading rule!
								vprint "^/^/-----------------------------------------"
								vprobe rule
								
								rule: compose/only/deep [
									(
										to-paren compose [vprint (join "argument: " name)]
									)
									[
									load-error-offset: [
										(rule) 
										load-error-offset: (
											to-paren [
												vprint "RULE OK!"
												vprint load-error-offset
												;ask "?"
											]
										)
									] | [
										( 
											to-paren compose/deep [
												load-error: rejoin [ "Expecting " (mold types) " for '" (to-string name) "' argument" ]
												vprint "RULE FAILED"
												vprint load-error-offset
												;ask "!"
											print load-error
											]
										)
									 	(src-rule) ; this will always fail if first rule failed!
									]
								]]
								
								vprobe rule
								
							]
							
							switch [
								if name [
									paren: compose [ (bind 'apply-arg 'flag) ]
									rule: compose/only [ (to-paren paren)]
								]
							]
						]
					]	

					append flag/args self

					vout
				]
			]

			;--------------------------------------------------------------------
			;---                                                              ---    
			;---                DEFINE ARG SPEC PARSING RULES                 ---
			;---                                                              ---    
			;--------------------------------------------------------------------
			
			;------------------------------------
			;-         text-flag-rule:
			;
			; this is a special flag which will load up ALL character data up to 
			; the next specified flag on the command-line (or its end).
			;------------------------------------
			text-flag-rule: [
				(vout) ; previous rule failed, get out of previous vin
				(
					vin "trying text subrule"
				)
				[
					here:
					( arg-name: arg-type: arg-default: none)
					copy arg-name word!
					here-error:
					'text!
					here-error:
					copy arg-default 0 1 block!
					here-error:
					(
						current-flag: make flag-ctx [
							name: flag-name
							init
						]
						vprint "VALID ARG FOUND" 
						vprobe here
						arg-name: arg-name/1
						vprint rejoin ["add flag argument: " arg-name " of type: " arg-type " with default: " arg-default]
						new-arg: make arg-ctx [
							name: arg-name
							required?: not block? arg-default
							default: all [arg-default pick first arg-default 1]
							types: [text!]
							flag: current-flag
							init
						]
						
					)
				] 
				[
					;----------
					; if we reach end of args block, then we finalize flag
					end 
					(
					current-flag/link-args
					current-flag/register
					vprint "---"
					vprint "FLAG:"
					vprobe current-flag
					)
				]
				|
				here:
				[
					;----------
					; we didn't reach the end for some reason, this means the spec is invalid
					;-                -arg flag error
					(
						error-msg: rejoin [
							"ERROR: unexpected item in '" flag-name "' specification.  Value: " 
							mold pick here-error 1 "^/ NEAR: " 
							remove head remove back tail mold here-error
						]
						make error! error-msg
					)
				]
			]
			
			
			
			;------------------------------------
			;-         flag-rule:
			;
			; a flag is the basic building block of command line parsing.  
			;
			; each word preceded by a dash ('-') is identified as a flag.
			;
			; note that scalar values (integers, decimals, etc) which 
			; start by a minus sign are NOT considered flags so cannot be 
			; used as flag names.
			; 
			;    (note: -1e isn't a valid scalar, so its a valid flag name)
			;------------------------------------
			flag-rule: [
				[ 
					;-------------------------------------
					;-            -switch flags
					;-------------------------------------
					(
						vin "trying flag switch subrule"
						current-flag: none
						new-arg: none
					)
					'switch! [
						[
							(vprint "SWITCH!")
							(
								current-flag: make flag-ctx [
									name: flag-name
									type: 'switch
									init
								]
							)
							(
								arg-value-when-unset: arg-name
								arg-value-when-set: none
								default-value: false
								arg-value-when-set: true 
							)
							here-error: [
								; assigning the flag to an argument is optional...
								; you can just look in args-ctx/flags if that is all you need.
								0 1 [
									copy arg-name word!
									here-error:
									0 1 [ into [
										; toggle setup (two values)
										[
											; value when set
											copy arg-value-when-set 
											skip
											here-error:
											
											; value when unset
											copy default-value
											skip 
											here-error:

											
											; there musn't be more than two values
											end
											
											(
												arg-value-when-set: first arg-value-when-set
												default-value: first default-value
												
												if find ['true | 'false | 'on | 'off] default-value [
													default-value: do default-value
												]
												if find ['true | 'false | 'on | 'off] arg-value-when-set [
													arg-value-when-set: do arg-value-when-set
												]
												
												vprint "^^---^/TOGGLE-type switch flag setup" 
												vprobe arg-name
												vprint "-"
												v??  arg-value-when-set
												vprint "-"
												v??  default-value ; default value
											)
										] 
										|
										
										; boolean setup
										[
											; default value (must be boolean)
											copy default-value ['true | 'false | 'on | 'off]
											here-error:
											
											; there must not be more than one value
											end
											(
												default-value: do first default-value
												arg-value-when-set: not default-value
												
												vprint "^^---^/BOOLEAN-type switch flag setup" 
												vprobe arg-name
												vprint "-"
												v??  arg-value-when-set
												vprint "-"
												v??  default-value ; default value
											)
											
										]
										
									]]	
								]
								here-error:
								(
									; create the flag's argument if its specified and valid
									if arg-name [
										arg-name: arg-name/1
										vprint rejoin ["add flag argument: " arg-name " of type: " arg-type " with default: " arg-default]
										new-arg: make arg-ctx [
											name: arg-name
											type: 'switch
											default: default-value
											value: arg-value-when-set
											flag: current-flag
											required?: false
											init
										]
									]
									
									vprobe new-arg
								)
							]
						]
						
						[
							;----------
							; if we reach end of args block, then we finalize flag
							end 
							(
							current-flag/link-args
							current-flag/register
							vprint "---"
							vprint "FLAG:"
							vprobe current-flag
							)
						]
						|
						here:
						[
							;----------
							; we didn't reach the end for some reason, this means the spec is invalid
							;-                -switch  error
							(
								error-msg: rejoin [
									"ERROR: unexpected item in '" flag-name "' specification.  Value: " 
									mold pick here-error 1 "^/ NEAR: " 
									remove head remove back tail mold here-error
								]
								make error! error-msg
							)
						]
					]
				]|[
					;-------------------------------------
					;-            -arg flags
					;-------------------------------------
					;

					(vout) ; previous rule failed, get out of previous vin
					(
						vin "trying flag value subrule"
						current-flag: make flag-ctx [
							name: flag-name
							init
						]
					)
					some [
						here:
						( arg-name: arg-type: arg-default: none)
						copy arg-name word!
						here-error:
						copy arg-type any spec-datatypes
						here-error:
						copy arg-default 0 1 block!
						here-error:
						(
							vprint "VALID ARG FOUND" 
							vprobe here
							arg-name: arg-name/1
							vprobe arg-type
							vprint rejoin ["add flag argument: " arg-name " of type: " mold arg-type " with default: "  arg-default]
							new-arg: make arg-ctx [
								name: arg-name
								required?: not block? arg-default
								default: all [arg-default pick first arg-default 1]
								types: arg-type
								flag: current-flag
								init
							]
							
						)
					] 
					[
						;----------
						; if we reach end of args block, then we finalize flag
						end 
						(
						current-flag/link-args
						current-flag/register
						vprint "---"
						vprint "FLAG:"
						vprobe current-flag
						)
					]
					|
					here:
					[
						;----------
						; we didn't reach the end for some reason, this means the spec is invalid
						;-                -arg flag error
						(
							error-msg: rejoin [
								"ERROR: unexpected item in '" flag-name "' specification.  Value: " 
								mold pick here-error 1 "^/ NEAR: " 
								remove head remove back tail mold here-error
							]
							make error! error-msg
						)
					]
				]
				(vout)
			]
			
			;------------------------------------
			;-        type-rule:
			;-------------------------------------
			type-rule: [
				(vin)
				(
					vprint "entering subrule flag"
				)
				copy arg-default block!
				(
					vprint rejoin ["add argument: trailing-" flag-type " with default: " arg-default]
				)
				(vout)
			]
			




			;--------------------------------------------------------------------
			;---                                                              ---    
			;---                          PARSE SPEC                          ---
			;---                                                              ---    
			;--------------------------------------------------------------------
			;-        PARSE SPEC
			vprobe parse spec [
				(subrule: none) 
				any [
					here:
					(vin rejoin ["PARSING: " head replace/all mold pick here 1 "^/" " "])
					[
						copy flag-name word! ( 
							vprint "flag name detected"
							flag-name: first flag-name
							vprobe flag-name
							subrule: flag-rule 
						)
						(vout)
					]|[
						into subrule
						(vout)
					]|[
						copy flag-type datatype! (
							vprint "trailing datatype values detected"
							vprobe flag-type
							subrule: type-rule 
						)
						(vout)
					]
					here:
					(
						to-error rejoin ["ERROR, invalid template near: " mold here]
					)
				]
			]
			
			
			
			;--------------------------------------------------------------------
			;---                                                              ---    
			;---                     PARSE COMMAND LINE                       ---
			;---                                                              ---    
			;--------------------------------------------------------------------
			;-        PARSE CMD
			vprint "^/^/---------------------------------^/PARSING COMMAND LINE ARGUMENTS^/---------------------------------" 

			; remove trailing '|
			remove back tail cmd-line-rule
			
			cmd-line-rule: compose/only copy [value-rules/spacer some (cmd-line-rule)]
			vprobe mold/all cmd-line-rule
			
			
			args-ctx: context args-ctx
			; temporary
			arg-load-blk: copy []
			vprobe args
			
			;probe args-ctx
			
			unless vprobe parse/all args cmd-line-rule [
				args-ctx/*error: rejoin [
					"Command-line error in '" flag-name "' flag"
					"^/Description: " load-error
					"^/Location : "	args
					"^/         "
					head insert/dup copy "" " " index? load-error-offset 
					;#"^^"
					;"A"
					;#"^(a4)" ; ยค
					"_" #"^(18)" "_"  ; 
					"^/"
					
				]
				vprint "^/^/"
				vprint error-msg
				vprint "^/^/"
			]
			vprobe arg-load-blk
			vprobe args-ctx
			
		][
			;--------------------------------------------------------------------
			;---                                                              ---    
			;---                args-context() error recovery                 ---
			;---                                                              ---    
			;--------------------------------------------------------------------
			vprint "------------------------"
			catch-error: false
			either catch-error [
				spec-error: disarm spec-error
				vprint spec-error/arg1
			][
				spec-error
			]
		]
		args-ctx
	]
]]]