Is it possible to detect esc key with ask function? - rebol

Would like to detect esc key to escape the forever loop in pseudo code:
forever [
url: ask "Url: "
if (url = esc) [
break
]
]
Is this possible ?

There is no simple answer, you must use own console-port to handle it correctly, here is the part of it taken from one of my old projects:
REBOL [title: "Console port"]
set 'ctx-console make object! [
system/console/busy: none
system/console/break: false
buffer: make string! 512
history: system/console/history
prompt: "## " ;system/console/prompt
spec-char: none
port: none
init: func[][
port: open/binary [scheme: 'console]
set 's-print get in system/words 'print
set 's-prin get in system/words 'prin
set 'prin func[msg /err /inf /user user-data][
ctx-console/clear-line
s-prin reform msg
ctx-console/print-line
]
set 'print func[msg /err /inf /user user-data][
prin rejoin [reform msg newline]
]
s-prin prompt
]
print-line: func[][
s-prin rejoin [ prompt head buffer "^(1B)[" length? buffer "D"]
]
clear-line: func[][
s-prin rejoin [
"^(1B)[" (
(index? buffer) +
(length? prompt) +
(length? buffer))
"D^(1B)[K"
]
]
key-actions: make block! [
#{08} [;BACK
if 0 < length? head buffer [
buffer: remove back buffer
s-prin rejoin [
"^(back)^(1B)[K"
buffer
"^(1B)["
length? buffer "D"
]
]
]
#{7E} [;HOME
s-prin rejoin ["^(1B)[" (index? buffer) - 1 "D"]
buffer: head buffer
]
#{7F} [;DELETE
buffer: remove buffer
s-prin rejoin ["^(1B)[K" buffer "^(1B)[" length? buffer "D"]
]
#{1B} [;ESCAPE
spec-char: copy/part port 1
either spec-char = #{1B} [
print "ESCAPE"
clear-line
set 'print :s-print
set 'prin :s-prin
system/console/break: true
on-escape
][
switch append spec-char copy/part port 1 [
#{5B41} [;ARROW UP
if not tail? history [
clear-line
clear head buffer
s-prin join prompt buffer: copy history/1
history: next history
buffer: tail buffer
]
]
#{5B42} [;ARROW DOWN
clear-line
buffer: head buffer
clear buffer
if all [
not error? try [history: back history]
not none? history/1
] [
buffer: copy history/1
]
s-prin join prompt buffer
buffer: tail buffer
]
#{5B43} [;ARROW RIGHT
if not tail? buffer [
s-prin "^(1B)[C"
buffer: next buffer
]
]
#{5B44} [;ARROW LEFT
if 1 < index? buffer [
s-prin "^(1B)[D"
buffer: back buffer
]
]
]
]
]
]
do-command: func[comm /local e][
set/any 'e attempt compose [do (comm)]
if all [
not unset? 'e
value? 'e
not object? :e
not port? :e
not function? :e
][
print head clear skip rejoin [system/console/result mold :e] 127
if (length? mold :e) > 127 [
print "...^/"
]
]
]
on-enter: func[input-str /local e][
print rejoin [system/console/prompt input-str]
do-command input-str
]
on-escape: func[][halt]
process: func[/local ch c tmp spec-char err][
ch: to-char pick port 1
either (ch = newline) or (ch = #"^M") [;ENTER
tmp: copy head buffer
if empty? tmp [return none]
history: head history
if any [empty? history tmp <> first history ] [
insert history tmp
]
clear-line
buffer: head buffer
clear buffer
print-line
on-enter tmp
][
switch/default to-binary ch key-actions [
either tail? buffer [
s-prin ch ;either local-echo [ch]["*"]
][
s-prin rejoin ["^(1B)[#" ch]
]
buffer: insert buffer ch
]
]
]
]
ctx-console/init
;and now do something with your own console:
wait-list: reduce [ctx-console/port]
forever [
attempt [ready: wait/all wait-list]
if ready [
ctx-console/process
]
]
You will probably like to change the ctx-console/on-escape and ctx-console/on-enter functions.

As a pure console application, I'm pretty sure the answer is no.
esc is used to cancel the execution of the script.
You can disable that use of esc....
system/console/break: false
....And the esc key now does nothing.
If you switch to REBOL/VIEW and are happy to use a pop up request-text box rather than a console ask line, then you may be able to trap esc using insert-event-func.

Related

file stop read when line reached

I have the code below to begin read the rest of the file after I have found the line containing the string "start"
foreach line clines [
if find [%start] line [
print line
]
]
I can't figure it out from the documentation.. what's going on here? seems logical to me.
You have always to read the file before you can search in the content of the file.
Maybe you are looking for something like
clines: read/lines %myfile
found: false
foreach line clines [
if any [
found
found: find line "start"
] [
print line
]
]
an other way would be
cfile: read %myfile
print all [
cfile: find cfile "start"
find/tail cfile lf
]

In Rebol, what is the idiomatic way to read a text file line by line?

For the purpose of reading a text file line by line, without loading the entire file into memory, what is the common way to do this in Rebol?
I am doing the following, but I think (correct me if I'm wrong) that it loads the whole file into memory first:
foreach line read/lines %file.txt [ print line ]
At least with Rebol2
read/lines/direct/part %file.txt 1
should come near to what you want
but if you want all lines one line after the other, it should be like
f: open/lines/direct %test.txt
while [l: copy/part f 1] [print l]
In theory you can supersede any function, even natives. I will try to give a new foreach
foreach_: :foreach
foreach: func [
"Evaluates a block for each value(s) in a series or a file for each line."
'word [get-word! word! block!] {Word or block of words to set each time (will be local)}
data [series! file! port!] "The series to traverse"
body [block!] "Block to evaluate each time"
/local port line
] [
either any [port? data file? data] [
attempt [
port: open/direct/lines data
while [line: copy/part port 1] [
set :word line
do :body
line
]
]
attempt [close port]
] [
foreach_ :word :data :body
]
]
Probably the set :word line part and the attempt should be more elaborated in order to avoid name clashes and get meaningful errors.
Yes open is the way to go. However like sqlab touches on the necessary /lines & /direct refinements are not present in Rebol 3 open (yet).
The good news though is that you can still use open to read in large files in Rebol 3 without these refinements...
file: open %movie.mpg
while [not empty? data: read/part file 32000] [
;
; read in 32000 bytes from file at a time
; process data
]
close file
So you just need to wrap this up into a buffer and process a line at a time.
Here's a crude working example I've put together:
file: open/read %file.txt
eol: newline
buffer-size: 1000
buffer: ""
lines: []
while [
;; start buffering
if empty? lines [
;; fill buffer until we have eol or EOF
until [
append buffer to-string data: read/part file buffer-size
any [
empty? data
find buffer eol
]
]
lines: split buffer eol
buffer: take/last lines
]
line: take lines
not all [empty? data empty? buffer]
][
;; line processing goes here!
print line
]
close file

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

I thought this parsing would be simple

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

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"