I thought this parsing would be simple - rebol

... and I'm hitting the wall, I don't understand why this doesn't work (I need to be able to parse either the single tag version (terminated with />) or the 2 tag versions (terminated with ) ):
Rebol[]
content: {<pre:myTag attr1="helloworld" attr2="hello"/>
<pre:myTag attr1="helloworld" attr2="hello">
</pre:myTag>
<pre:myTag attr3="helloworld" attr4="hello"/>
}
spacer: charset reduce [#" " newline]
letter: charset reduce ["ABCDEFGHIJKLMNOPQRSTUabcdefghijklmnopqrstuvwxyz1234567890="]
rule: [
any [
{<pre:myTag}
any [any letter {"} any letter {"}] mark:
(print {clipboard... after any letter {"} any letter {"}} write clipboard:// mark input)
any spacer mark: (print "clipboard..." write clipboard:// mark input) ["/>" | ">"
any spacer </pre:myTag>
]
any spacer
(insert mark { Visible="false"})
]
to end
]
parse content rule
write clipboard:// content
print "The end"
input

In this case, the problem isn't your rule - it's that your 'insert after each tag changes alters the position at the point you do the insert.
To illustrate:
>> probe parse str: "abd" ["ab" mark: (insert mark "c") "d"] probe str
false
"abcd"
== "abcd"
The insert is correct, but after the insert, the parse rule is still at position 2, and before where there was just "d", there is now "cd" and the rule fails. Three strategies:
1) Incorporate the new content:
>> probe parse str: "abd" ["ab" mark: (insert mark "c") "cd"] probe str
true
"abcd"
== "abcd"
2) Calculate the length of the new content and skip:
>> probe parse str: "abd" ["ab" mark: (insert mark "c") 1 skip "d"] probe str
true
"abcd"
== "abcd"
3) Change the position after the manipulation:
>> probe parse str: "abd" ["ab" mark: (mark: insert mark "c") :mark "d"] probe str
true
"abcd"
== "abcd"
Number 2) would be the quickest in your case as you know your string length is 16:
rule: [
any [
{<pre:myTag} ; opens tag
any [ ; eats through all attributes
any letter {"} any letter {"}
]
mark: ( ; mark after the last attribute, pause (input)
print {clipboard... after any letter {"} any letter {"}}
write clipboard:// mark
input
)
any spacer mark: ; space, mark, print, pause
(print "clipboard..." write clipboard:// mark input)
[ ; close tag
"/>"
|
">" any spacer </pre:myTag>
]
any spacer ; redundant without /all
(insert mark { Visible="false"})
16 skip ; adjust position based on the new content
]
to end
]
Note: this is the same rule as yours with just [16 skip] added.

Related

Can rebol trim remove blank lines without removing CRLF?

I wanted to use trim to remove blank lines:
line 1
line 2
to get
line1
line2
but using
trim/lines
does also remove CRLF. So is there another way to use trim for that purpose ?
You could use PARSE:
parse string-with-newlines [
any [
crlf remove some crlf
| newline remove some newline
| skip
]
]
It may be faster to use charsets though:
text: complement charset crlf
parse string-with-newlines [
any [
some text
| crlf any crlf
| newline remove any newline
]
]
replace/all {Line1^/^/Line2} {^/^/} {^/}
No way just with trim, but here a solution with removeach and also, also removing leading LFs
trim-emptyline: func [
str [string!]
/local lfb4 lfnow c
] [
lfb4: true
remove-each c str [also all [lfnow: lf = c lfb4] lfb4: lfnow]
str
]

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.

Problem with context and property

Let's say I want to generate this output:
public String toString() {
return this.getFirstName() + "," + this.getLastName() + "," + this.getAge();
}
from the template below and a custom recursive build-markup function:
template-toString: {this.get<%property%>() <%either not context.build-markup/EOB [{+ "," +}][""]%> }
build-markup/vars template-toString [property] ["FirstName" "LastName" "Age"]
My problem is to avoid the last element to be concatenate with {+ "," +}
My idea was to use a context.build-markup with an EOB property (End Of Block) that would be set to true when last element is processed. Then I could use in template-toString above either not context.build-markup/EOB [{+ "," +}][""] to concatenate or not with {+ "," +} :
context.build-markup: context [
EOB: false
set 'build-markup func [
{Return markup text replacing <%tags%> with their evaluated results.}
content [string! file! url!]
/vars block-fields block-values
/quiet "Do not show errors in the output."
/local out eval value n max i
][
out: make string! 126
either not vars [
content: either string? content [copy content] [read content]
eval: func [val /local tmp] [
either error? set/any 'tmp try [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)
]
]
][
n: length? block-fields
self/EOB: false
actions: copy []
repeat i n [
append actions compose/only [
;set in self 'EOB (i = n)
set in system/words (to-lit-word pick (block-fields) (i)) get pick (block-fields) (i)
]
]
append actions compose/only [
append out build-markup content
]
foreach :block-fields block-values actions
if any [(back tail out) = "^/" (back tail out) = " " (back tail out) = "," (back tail out) = ";" (back tail out) = "/" (back tail out) = "\"] [
remove back tail out
]
]
out
]
]
But my attempt failed (so I commented ;set in self 'EOB (i = n) because it doesn't work). How to correct the code to get what I want ?
I'm quite certain you could be achieving your goal in a cleaner way than this. Regardless, I can tell you why what you're doing isn't working!
Your n is the expression length? block-fields, and your repeat loop goes up to n. But block-fields contains the single parameter [property]! Hence, it loops from 1 to 1.
You presumably wanted to test against something enumerating over block-values (in this example a range from 1 to 3) and then handle it uniquely if the index reached 3. In other words, your set in self 'EOB expression needs to be part of your enumeration over block-values and NOT block-fields.
This would have given you the behavior you wanted:
n: length? block-values
i: 1
foreach :block-fields block-values compose/only [
set in self 'EOB equal? i n
do (actions)
++ i
]
This absolutely won't work:
append actions compose/only [
set in self 'EOB (i = n)
set in system/words (to-lit-word pick (block-fields) (i)) get pick (block-fields) (i)
]
...because you are dealing with a situation where i and n are both 1, for a single iteration of this loop. Which means (i = n) is true. So the meta-code you get for "actions" is this:
[
set in self 'EOB true
set in system/words 'property get pick [property] 1
]
Next you run the code with a superfluous composition (because there are no PAREN!s, you could just omit COMPOSE/ONLY):
append actions compose/only [
append out build-markup content
]
Which adds a line to your actions meta-code, obviously:
[
set in self 'EOB true
set in system/words 'property get pick [property] 1
append out build-markup content
]
As per usual I'll suggest you learn to use PROBE and PRINT to look and check your expectations at each phase. Rebol is good about dumping variables and such...
You seem to making something simple very complicated:
>> a: make object! [
[ b: false
[ set 'c func[i n] [b: i = n]
[ ]
>> a/b
== false
>> c 1 4
== false
>> a/b
== false
>> c 1 1
== true
>> a/b
== true

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"