How to test for unset parameter in rebol / red within a function? - rebol

When I call f without parameter, I got the error Script Error: if does not allow unset! for its then-blk argument why ?
f: func['p [string! unset!]][
if unset? 'p print "unset"
]

'p evaluates to the word p. In order to test the type of the value referred by p, you need to use :p and provide a proper body block for if:
f: func ['p [string! unset!]][
if unset? :p [print "unset"]
]
>> f "123"
== none
>> f
unset

Related

How to use rebol/red bind to pass a block argument to another function?

I don't really understand if it can be done with bind or anything else, the documentation http://www.rebol.com/docs/words/wbind.html doesn't help as for me. I want to pass block argument from f2 to f1 :
f1: func[block][
foreach b block [
print b
]
]
f2: func[][
a: 1
b: 2
block: [a b]
f1 block
]
When calling
f2
It prints out
a
b
How to make it print out
1
2
while passing [a b] (I don't want to pass [1 2] since it can change all the time and that is not elegant) ? Is it possible with bind or anything else ?
f1: func[block /local i][
foreach i block [
print get i
]
]
or
f1: func[block /local b][
foreach b reduce block [
print b
]
]
There is a need for the local word in Red, as the foreach variable word in Red is not local to the foreach loop.

Change file paths

I want to change [%a/b] to [%a/c].
Basically, the same as Change path or refinement, but with file! instead:
I want to change the a/b inside a block to a/c
test: [a/b]
In this case, either change next test/1 'c or test/1/2: 'c works.
But not when test is a file!:
>> test: [%a/b]
== [%a/b]
>> test/1
== %a/b
>> test/1/2 ; can't access 2nd value
== %a/b/2
>> next first test ; not quite what you expect
== %/b
Trying to change it gives not something you'd expect:
>> change next test/1 'c
== %b
>> test
== [%acb]
You are confusing path! and file! series, they can look similar, but their nature are very different.
A path! is a collection of values (often word! values) separated by a slash symbol, a file! is a collection of char! values. Slash characters in file! series are just characters, so file! has no knowledge about any sub-structures. It has (mostly) the semantics of string! series, while path! has the semantics of a block! series.
Now that this is cleared, about the test/1/2 result, path notation on a file! series has a different behavior than on string!, it will do a smart concatenation instead of acting as an accessor. It's called smart because it will nicely handle extra slash characters present in left and right parts. For example:
>> file: %/index.html
== %/index.html
>> path: %www/
== %www/
>> path/file
== %www/file
>> path/:file
== %www/index.html
Same path notation rule applies to url! series too:
>> url: http://red-lang.org
== http://red-lang.org
>> url/index.html
== http://red-lang.org/index.html
>> file: %/index.html
== %/index.html
>> url/:file
== http://red-lang.org/index.html
So for changing the nested content of test: [%a/b], as file! behaves basically as string!, you can use any available method for strings to modify it. For example:
>> test: [%a/b]
== [%a/b]
>> change skip test/1 2 %c
== %""
>> test
== [%a/c]
>> change next find test/1 slash "d"
== %""
>> test
== [%a/d]
>> parse test/1 [thru slash change skip "e"]
== true
>> test
== [%a/e]
Files are string types and can be manipulated in the same way you'd modify a string. For example:
test: [%a/b]
replace test/1 %/b %/c
This is because file! is an any-string!, not any-array!
>> any-string? %a/c
== true
>> any-array? 'a/c
== true
So the directory separator '/' in a file! doesn't mean anything special with the action CHANGE. So 'a', '/', and 'b' in %a/b are treated the same way, and the interpreter isn't trying to parse it into a two segment file path [a b].
While for a path!, because it's an array, each component is a rebol value, and the interpreter knows that. For instance, 'bcd' in a/bcd will be seen as a whole (a word!), instead of three characters 'b', 'c' and 'd'.
I agree that the file! being an any-string! is not convenient.
Here is a maybe cumbersome solution, but suitable for directories treating them as files
test/1: to-file head change skip split-path test/1 1 %c

How do I write contents of a variable to a text file in Rebol 2?

Newbie question here...
I'd like to write the output of the "what" function to a text file.
So here is what I have done:
I've created a variable called "text" and assigned the output of "what" to it
text: [what]
Now I want to write the content of the "text" variable to a txt file...
Any help is appreciated. Thanks in advance!
the easiest way to write the output of statements to a file is to use
echo %file.log
what
with echo none you end this
>> help echo
USAGE:
ECHO target
DESCRIPTION:
Copies console output to a file.
ECHO is a function value.
ARGUMENTS:
target -- (Type: file none logic)
(SPECIAL ATTRIBUTES)
catch
Unfortunately there is not really a value returned from the what function:
Try the following in the console:
print ["Value of `what` is: " what]
So write %filename.txt [what] will not work.
Instead, what you could do is to look at the source of what
source what
which returns:
what: func [
"Prints a list of globally-defined functions."
/local vals args here total
][
total: copy []
vals: second system/words
foreach word first system/words [
if any-function? first vals [
args: first first vals
if here: find args /local [args: copy/part args here]
append total reduce [word mold args]
]
vals: next vals
]
foreach [word args] sort/skip total 2 [print [word args]]
exit
]
See that this function only prints (it doesn't return the values it finds) We can modify the script to do what you want:
new-what: func [
"Returns a list of globally-defined functions."
/local vals args here total collected
][
collected: copy []
total: copy []
vals: second system/words
foreach word first system/words [
if any-function? first vals [
args: first first vals
if here: find args /local [args: copy/part args here]
append total reduce [word mold args]
]
vals: next vals
]
foreach [word args] sort/skip total 2 [append collected reduce [word tab args newline]]
write %filename.txt collected
exit
]
This function is a little hackish (filename is set, but it will return what you want). You can extend the function to accept a filename or do whatever you want. The tab and newline are there to make the file output prettier.
Important things to notice from this:
Print returns unset
Use source to find out what functions do
write %filename value will write out a value to a file all at once. If you open a file, you can write more times.
Fairly elementary: use write if you just want to save some text, read to recover it; use save if you want to store some data and use load to recover it.
>> write %file.txt "Some Text"
>> read %file.txt
== "Some Text"
>> text: [what]
>> save/all %file.r text
>> load %file.r
== [what]
You can get more information on each word at the prompt: help save or view online: load, save, read and write.

Is it possible to override rebol path operator?

It is possible to overide rebol system words like print, make etc., so is it possible to do the same with the path operator ? Then what's the syntax ?
Another possible approach is to use REBOL meta-programming capabilities and preprocess your own code to catch path accesses and add your handler code. Here's an example :
apply-my-rule: func [spec [block!] /local value][
print [
"-- path access --" newline
"object:" mold spec/1 newline
"member:" mold spec/2 newline
"value:" mold set/any 'value get in get spec/1 spec/2 newline
"--"
]
:value
]
my-do: func [code [block!] /local rule pos][
parse code rule: [
any [
pos: path! (
pos: either object? get pos/1/1 [
change/part pos reduce ['apply-my-rule to-block pos/1] 1
][
next pos
]
) :pos
| into rule ;-- dive into nested blocks
| skip ;-- skip every other values
]
]
do code
]
;-- example usage --
obj: make object! [
a: 5
]
my-do [
print mold obj/a
]
This will give you :
-- path access --
object: obj
member: a
value: 5
--
5
Another (slower but more flexible) approach could also be to pass your code in string mode to the preprocessor allowing freeing yourself from any REBOL specific syntax rule like in :
my-alternative-do {
print mold obj..a
}
The preprocessor code would then spot all .. places and change the code to properly insert calls to 'apply-my-rule, and would in the end, run the code with :
do load code
There's no real limits on how far you can process and change your whole code at runtime (the so-called "block mode" of the first example being the most efficient way).
You mean replace (say)....
print mold system/options
with (say)....
print mold system..options
....where I've replaced REBOL's forward slash with dot dot syntax?
Short answer: no. Some things are hardwired into the parser.

Rebol: How to use local variables with build-markup function?

Is there a way to do so including creating an other build-markup function ?
Sadly,build-markup uses only global variables: link text says: Note that variables used within tags are always global variables.
Here's a slightly cranky way of doing it using an inner object (bm-1 demonstrates the problem: a and b are printed with their global values; bm-2 is the cranky work around):
a: "global-a"
b: "global-b"
bm-1: func [a b][
print build-markup "<%a%> <%b%>"
]
bm-2: func [a b][
cont: context [
v-a: a
v-b: b
]
print build-markup "<%cont/v-a%> <%cont/v-b%>"
]
bm-1 "aaa" "bbb"
bm-2 "aaa" "bbb"
REBOL3 has reword rather than build-markup. That is much more flexible.
I've patched the build-markup function to be able to use local contexts:
build-markup: func [
{Return markup text replacing <%tags%> with their evaluated results.}
content [string! file! url!]
/bind obj [object!] "Object to bind" ;ability to run in a local context
/quiet "Do not show errors in the output."
/local out eval value
][
content: either string? content [copy content] [read content]
out: make string! 126
eval: func [val /local tmp] [
either error? set/any 'tmp try [either bind [do system/words/bind load val obj] [do val]] [
if not quiet [
tmp: disarm :tmp
append out reform ["***ERROR" tmp/id "in:" val]
]
] [
if not unset? get/any 'tmp [append out :tmp]
]
]
parse/all content [
any [
end break
| "<%" [copy value to "%>" 2 skip | copy value to end] (eval value)
| copy value [to "<%" | to end] (append out value)
]
]
out
]
Here are some example usages:
>> x: 1 ;global
>> context [x: 2 print build-markup/bind "a <%x%> b" self]
"a 2 b"
>> print build-markup/bind "a <%x%> b" context [x: 2]
"a 2 b"