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
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.
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
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.
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.
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"