AI
Animation
CGI
Compression
Console
Database
Debug
Dialects
Dialogs
Editor
Email
Encryption
Extension
External Library
File
File Handling
Files
Financial
FTP
Game
Games
Graphics
GUI
HTML
HTTP
Internet
LDC
Markup
Math
Module
Network
Networking
None
Other - Net
Parse
Patch
Printing
Protocol
Rebol
Scheme
Scientific
SDK
Security
Shell
Sound
SQL
TCP
Testing
Text
Text Processing
UI
User Interface
Util
Utility
VID
Visualization
Web
Win API
X-File
XML
rebol [
	; -- basic rebol header --
	file:		%encompass.r
	version:	1.0.3
	date:		25-Nov-2003
	author:		"Maxim Olivier-Adlhoch"
	title:		"encompass function"
	purpose: 	"Patch/Extend/Restrict any function by transparently enclosing it within another."


	; -- rebol.org header --
	library: [
	        level: 'advanced
	        platform: 'all
	        type: 'function
	        domain: [extension patch]
	        tested-under: [view 1.2.10 w2k]
	        support: none
	        license: [lgpl]
	        see-also: none
	    ]
	

	
	; -- extended rebol header --
	notes: "Remove example at the end when using it in your code"
	copyright:	"Copyright (c) 2003 Maxim Olivier-Adlhoch"
	web: "http://www.rebol.it/~steel"
	e-mail: "moliad@aei.ca"
	original-author: "Maxim Olivier-Adlhoch"
	history: {
		v1.0.0:
			-basic functionality works
		v1.0.1:
			-encompassing function now always returns a value.
			-/silent prevents enclosed function from assigning a value to rval
			-/args can now add your own parameters and refinements to spec of enclosed function
			-/rval always added to spec
			-/local will now be removed from enclosed function's spec along with all local variables it defines.
				use /args to add your own.
		v1.0.2:
			-totally rewritten code.  integrated all 3 loops into 1
			-fixed a bug with /local handling
			-changed debug output
			-changed example code, to make it more obvious
		v1.0.3:
			-added missing local words in function!'s locals block

	}
	license:   {This tool is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation;
version 2.1 of the License.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

you can also get a complete copy of this license at
http://www.opensource.org/licenses/lgpl-license.php
}
	disclaimer: {
This tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
}
]


;--------------------------
;- encompass
;------
; enclose a specified function within a new function with optional leading and trailing processing.
; it will safely carry over only refinements which are really used and you can even supply your own (which
; stay local to the encompassing function).
; note that in any case, a refinement called rval is always added to template, in order to create a local variable used by returns.
encompass: function [
	func-name [word!] "the name of a function to encompass, specified as a word. Must exist in global namespace."
	/args opt-args [block!] "a block of optional args which your pre or post processing will use.  These are, of couse, not sent to enclosed function."
	/pre pre-process "code you want to execute BEFORE the enclosed function. context is kept, so it is safe with objects."
	/post post-process "code you want to execute AFTER the enclosed function. context is kept, so it is safe with objects."
	/silent "notify that the enclosed function DOES NOT return a value (like print). Rval is still returned and will be none by default, unless your post process plays with it."
	/debug "prints out various data and makes enclosed function print itself before execution"
][
	; local variables
	blk dt func-args func-ptr func-body last-ref item params-blk refinements word arguments args-blk][
	if debug [
		print "^/^/----------------------------------------------------------------------"
		print "encompass()^/"
	]
	
	;----------------
	; find function to override
	;----------------
		func-ptr: get in system/words func-name
	
	;----------------
	; make sure we really have a function to work with
	;----------------
		if not any-function? :func-ptr [print "  error... funcptr is not a function value or word" return none]
	
	;----------------
	; reconstruct args block
	;----------------
		arguments: third :func-ptr ; get enclosed function's arg block
		func-args: copy []   ; the actual block which will be used for sub-func
		last-ref: none
		
		; prepare function...
		args-blk: copy compose [
			([('system)])
			([('words)])
			(to paren! to-lit-word func-name)
		]
		params-blk: copy [] ; stores all info about the params
		
		; prepare datablocks to construct path
		FOREACH item arguments [
			; block values in arguments are always datatype casting specifications
			SWITCH/default TYPE?/word item [
				block! [
					; change all datatype values into word values.
					blk: copy []
					FOREACH dt item [
						word: MOLD dt
						APPEND blk TO-WORD word
					]
					APPEND/only func-args blk
				]
			
				refinement! [
					last-ref: item
					; never include local word setup, this is local to enclosed function...
					if last-ref <> /local [
						APPEND func-args item
						append/only args-blk to paren! compose/deep [either (to-word item) [(to-lit-word item)][]]
					]
				]
			
				word! [
				
					either last-ref [
						; never include local word setup, this is local to enclosed function...
						if last-ref <> /local [
							append/only params-blk to paren! copy compose/deep [either (to-word last-ref) [(item)][]]
							append func-args item
						]
					][
						append/only params-blk to paren! item
						append func-args item
					]
					
				]
			][
				; default block
				append func-args item
			]
		]


	;----------------
	; actually create function body
	;----------------
		; currently only supports global namespace words. 
		; using this  system/words/ notation, prevents circular references when using methods
		; in objects which have the same name as the one which is in global namespace :-)
		
		; nest refinement and parameters into various paren and blocks to create actual function call
		blk: append append/only copy [] to paren! compose/deep [ to-path compose [(args-blk)]] params-blk
		
		;func-body: insert copy [] 
		
		;create outer body block,
		func-body: append copy [] compose [
			;include pre-process (if any)
			(either pre [pre-process][])
			; in debug mode this prints the final internal function call, just before calling it
			enclosed-func: (either debug ['probe][])
			; add main function body, created above
			compose (append/only copy [] blk)
			; user wants the internal command's returned value (rval:)
			(either silent [][to-set-word 'rval])
			; call the dynamically generated command.
			do enclosed-func
			; insert post-process (if any)
			(either post [post-process][])
			; add the return statement
			return rval
		]
		
	;----------------
	; add optional arguments which your pre or post processing might need...
	;----------------
		if args [
			; find point where parameters end and refinements start
			refinements: find func-args refinement!
			either refinements[
				func-args: refinements
			][
				; there are no refinements, so we add to the end
				func-args: tail func-args
			]
			insert func-args opt-args
		]
		
		append func-args [/rval]
		func-args: head func-args
		
	;----------------
	; debug information
	;----------------
		if debug [
			print "^/FUNCTION ARGUMENT SPEC:"
			probe func-args
			
			print "^/FINAL FUNCTION BODY:"
			probe func-body
			print "----------------------------------------------------------------------^/^/"
		]
		return func func-args func-body
]



;----------------
;--- examples ---
;----------------

; patches 'READ function so that it warns you of all file reads and asks confirmation.
old-read: :read
read: encompass/args/pre 'old-read [/safe] [
	if not safe [
		print "----------------- WARNING! --------------------"
		print "--                                           --"
		print "--         FILE READ ABOUT TO OCCUR          --"
		print "--                                           --"
		print "-----------------------------------------------"
		print ["-- path:" clean-path source]
		print "-----------------------------------------------"
		answer: ask "authorize (Y/N)?"
		if answer <> "Y" [
			ask "APPLICATION ABORTING, USER DID NOT ACCEPT FILE READ^/^/press enter to quit!^/^/"
			quit
		]
	]
]


; this shows that you do not have to know about any refinement or argument from source function
; in order to patch it.
print read/part %encompass.r 126

ask ""




            
            
        
Copyright © 2018 Rebol Software Foundation