I want to compile this script:
Red [File: %b.red]
context [
a: 123
hidden: 999
set 'global-exports context [
export-a: a
]
]
probe global-exports
But I get an error when trying to compile it with $ ./red-13oct19-a4ee537c -r b.red:
*** Red Compiler Internal Error: Script Error : Invalid path value: global-exports
*** Where: register-object
*** Near: [objects/context/global-exports: make object! [
a: none
hidden: none
]]
In general, you can wrap dynamic code with do [...] function, which treats a block of code like data and uses an interpreter to launch it at runtime:
Red [File: %b.red]
context [
a: 123
hidden: 999
do [ ;-- the code inside is run by interpreter at runtime
set 'global-exports context [
export-a: a
]
]
]
probe get 'global-exports ;-- dynamic retrieval
You also have to retrieve values dynamically, unless you initialize it statically (e.g. global-exports: none) somewhere earlier.
In this particular case also exchanging context with make object! will be sufficient:
Red [File: %b.red]
context [
a: 123
hidden: 999
set 'global-exports make object! [
export-a: a
]
]
probe global-exports
Compiling dynamic code will be possible with JIT compiler, which is planned in future, but not before Red version 1.0.
(thanks #9214 and #hiiamboris for the ideas on red/help gitter chat)
Related
I have a Rebol2 console app (Rebol Core) which I would like to disable the keyboard character echo to the console. The app is run from a minimal Linux kernel/initramfs, and is started by busybox inittab (not from terminal). It has a minimal console user interface using ansi codes for color, etc., and responds to menu selections via single key presses. To 'quiet' the console, I have the cursor turned off, and don't see the output of the key presses (until recently).
I previously thought I had the problem solved by calling 'stty -echo' within Rebol, but it actually does not work as I just discovered- there is one function that takes 5-10 seconds and can see the echoed key presses while waiting for the function to complete.
I'm not quite sure why I only see the echoed characters while this one function is running, but it is the only function which takes any amount of time. The keyboard is polled by opening the console:// in binary mode, waiting for a key press, then a switch statement to choose the function. The reading of the keys in binary/console seem to 'consume' the key echo-
minimal example, pressing 'a'-
cons: open/binary console://
first cons
== 97
(the value is returned as I want, and the char is not echoed, which is good- I think in most functions my keys are 'consumed' in the get-key loop, but the longer function does not get a chance to 'consume' them, and end up echoing to the console)
Is there some way to disable the console character echo inside Rebol2? I have looked in system/console and system/ports/input,output, but don't see anything obvious. My current workaround is to simply change the text color to match the background so any key presses are not visible while the specific function runs.
here is a minimal example of what I'm doing-
get-key: func [ /local cons ch ][
cons: open/binary console://
ch: lowercase to-string to-char first cons
all [ equal? ch "^[" append ch copy/part cons 2 ]
close cons
ch
]
forever [
switch get-key [
;up arrow
"^[[A" [ some-function1 ]
;down arrow
"^[[B" [ some-function2 ]
;enter
"^M" [ some-function3 ]
;quit
"q" [ break ]
]
]
The forever loop seems to 'consume' the keyboard input (nothing echoed), but if one of the functions takes any amount of time, any keyboard input will get echoed to the screen wherever the cursor happens to be. In most cases I never see any echoed characters as the time between calling get-key is minimal. I would also note that the echoed characters do not also show up in the subsequent call to get-key.
update-
here is a better sample of code to see the problem-
get-key: has [ cons ch ][
cons: open/binary console://
ch: lowercase to-string to-char first cons
prin rejoin [ "<" ch ">" ] ;show get-key chars
ch
]
long-func: does [ call/wait {sleep 10} ]
reb-func: does [ wait 10 ]
forever [
switch get-key [
"f" [ long-func ]
"r" [ reb-func ]
"q" [ break ]
]
]
I figured out that my 'long' function is using call's which can take a few seconds, so the problem arises when a call is used.
The above code, when run will show that keys are echoed only because they are printed in the get-key function (brackets), when the long-func is running, then keys are echoed outside of get-key (no brackets), and when done the get-key will process those keys also. Or simply run 'call/wait {sleep 10}' and you will get echoed keys while waiting, and also get the sames keys echoed by Rebol when the call returns. Keys are not echoed when reb-func runs, and get-key will process all buffered keys when reb-func is done. The keyboard input is being handled twice when call is used.
I have tried redirecting stdin/stdout in the call command (in the call string command, like at bash prompt), but have not found a combo that works. (My actual code runs call with /output/error to capture all output).
Without optimizing your code with waiting on the port and an awake function I guess your problem can be solved by placing opening and closing the console port outside your get-key function as in
get-key: func [ /local ch ][
ch: lowercase to-string to-char first cons
all [ equal? ch "^[" append ch copy/part cons 2 ]
ch
]
cons: open/binary [scheme: 'console]
forever [
switch get-key [
;up arrow
"^[[A" [ some-function1 ]
;down arrow
"^[[B" [ some-function2 ]
;enter
"^M" [ some-function3 ]
;quit
"q" [ break ]
]
]
close cons
ok, here is an optimized version including your second example
long-func: does [ call/wait {stty -echo ; sleep 10} ]
reb-func: does [ wait 10 ]
cons: open/binary [scheme: 'console]
cons/awake: func [port] [
key: to-char first port
print ["<" key ">"]
switch key [
#"f" [long-func]
#"r" [reb-func]
#"q" [break]
]
]
forever [
wait [cons]
]
you can see, that all keys are catched without extra echo
The rearranged console code is not necessary (and all keys are cached no matter which arrangement is used), although the ability to add an awake function is nice to know. In my real code, the get-key has a '/timeout t' option where I can do a 'wait [ cons t ]' and return a string (for extended key codes like up arrow) or none, which means I can also flush the console input before my switch get-key (so any keys pressed while running functions get flushed).
forever [
while [ get-key/timeout 0.1 ][] ;flush
switch get-key [ ;wait for key
...
The 'stty -echo' works well in the example given, and would seem to solve my problem, but I still see a few characters echoed if I press a bunch of keys while the long functions are running (I inserted {stty -echo; } in all commands). Somehow, in the Rebol call creating of processes (fork/exec I assume), tty input/output can still 'leak' i/o characters. Or maybe one of the programs called is opening a tty even though it inherits the file descriptors of the parent.
Here is what I ended up doing- changing the way I call commands so they run in background, but still wait for them to complete.
;used by other funcs to parse output of commands
set 'cmd-output "" ;output from 'call' commands
set 'cmd-error "" ;error from 'call commands
set 'cmd func [ str [string!] /local ret ][
--old--
;clear previous
cmd-output: copy ""
cmd-error: copy ""
;--new--
write %out ""
write %err ""
attempt [ delete %ret ]
;--old--
;ret: call/wait/output/error str cmd-output cmd-error
;--new-- stdout->out stderr->err exitcode->ret
call rejoin [ str { >out 2>err; echo -n $? > ret} ]
;wait for return code, (up to 20 seconds, should be plenty)
loop 200 [ all [ exists? %ret break ] wait 0.1 ]
cmd-output: read %out
cmd-error: read %err
ret: to-integer read %ret
--old--
;debug-cmd writes info to a logfile
debug-cmd str ret
ret
]
This works, as I cannot get any (unwanted) characters to show on the screen as before (and I guess it proves those characters were coming from the called processes).
I attempted to write a C-style for-loop in REBOL:
for [i: 0] [i < 10] [i: i + 1] [
print i
]
This syntax doesn't appear to be correct, though:
*** ERROR
** Script error: for does not allow block! for its 'word argument
** Where: try do either either either -apply-
** Near: try load/all join %/users/try-REBOL/data/ system/script/args...
Does REBOL have any built-in function that is similar to a C-style for loop, or will I need to implement this function myself?
The equivalent construct in a C-like language would look like this, but I'm not sure if it's possible to implement the same pattern in REBOL:
for(i = 0; i < 10; i++){
print(i);
}
Because of the rebol3 tag, I'll assume this question pertains to Rebol 3.
Proposed "CFOR" for Rebol 3
For Rebol 3, there is a proposal (which got quite a bit of support) for a "general loop" very much along the lines of a C-style for and therefore currently going under the name of cfor as well: see CureCode issue #884 for all the gory details.
This includes a much refined version of Ladislav's original implementation, the current (as of 2014-05-17) version I'll reproduce here (without the extensive inline comments discussing implementation aspects) for the sake of easy reference:
cfor: func [ ; Not this name
"General loop based on an initial state, test, and per-loop change."
init [block! object!] "Words & initial values as object spec (local)"
test [block!] "Continue if condition is true"
bump [block!] "Move to the next step in the loop"
body [block!] "Block to evaluate each time"
/local ret
] [
if block? init [init: make object! init]
test: bind/copy test init
body: bind/copy body init
bump: bind/copy bump init
while test [set/any 'ret do body do bump get/any 'ret]
]
General problems with user-level control structure implementations in Rebol 3
One important general remark for all user-level implementation of control constructs in Rebol 3: there is no analogue to Rebol 2's [throw] attribute in R3 yet (see CureCode issue #539), so such user-written ("mezzanine", in Rebol lingo) control or loop functions have problems, in general.
In particular, this CFOR would incorrectly capture return and exit. To illustrate, consider the following function:
foo: function [] [
print "before"
cfor [i: 1] [i < 10] [++ i] [
print i
if i > 2 [return true]
]
print "after"
return false
]
You'd (rightly) expect the return to actually return from foo. However, if you try the above, you'll find this expectation disappointed:
>> foo
before
1
2
3
after
== false
This remark of course applies to all the user-level implementation given as answers in this thread, until bug #539 is fixed.
There is an optimized Cfor by Ladislav Mecir
cfor: func [
{General loop}
[throw]
init [block!]
test [block!]
inc [block!]
body [block!]
] [
use set-words init reduce [
:do init
:while test head insert tail copy body inc
]
]
The other control structure that most people would use in this particular case is repeat
repeat i 10 [print i]
which results in:
>> repeat i 10 [print i]
1
2
3
4
5
6
7
8
9
10
I generally do no use loop very often, but it can be used to a similar extent:
>> i: 1
>> loop 10 [print ++ i]
1
2
3
4
5
6
7
8
9
10
Those are some useful control structures. Not sure if you were looking for cfor but you got that answer from others.
I have implemented a function that works in the same way as a C for loop.
cfor: func [init condition update action] [
do init
while condition [
do action
do update
]
]
Here's an example usage of this function:
cfor [i: 0] [i < 10] [i: i + 1] [
print i
]
For simple initial value, upper limit and step, following works:
for i 0 10 2
[print i]
This is very close to C for loop.
Here my function that execute cmd as a Rebol instructions :
exec-cmd: func [
cmd [ block! ] "Rebol instructions"
/local err
] [
if error? err: try [
do cmd
] [ print mold disarm err ]
]
When I launch the function, I've encountered the following error message :
** Script Error: err needs a value
** Where: exec-cmd
** Near: if error? err: try [
do cmd
]
How can I avoid this message and manage the error ?
When the Rebol default evaluator sees a sequence of a SET-WORD! followed by a "complete" expression, it will assign the result of that expression to the named word.
However, Rebol has the ability to return a special kind of "nothing" from a function called an UNSET!. For instance:
>> type? print {What "value" does a print return?}
What "value" does a print return?
== unset!
This is different from returning a NONE! value...because if you continue the chain of evaluation, the evaluator will not allow them in assignments.
>> foo: print {This isn't legal}
This isn't legal
** Script Error: foo needs a value
** Near: foo: print "This isn't legal"
Variables cannot actually "hold a value" of type UNSET!. UNSET! is just the "value type" that you will get if you try and access a variable that is not set. Regardless of the philosophical equivalence of whether there is a none value or not, the mechanical consequence is that if you want to allow an unset! value to effectively be "assigned" you have to do that "assignment" using the set function and the /any refinement:
>> set/any 'foo (print {This works...})
This works...
== unset!
But to be able to read from the value, you can't just reference it as the variable is now undefined. You need to use the corresponding get:
>> type? get/any 'foo
== unset!
Anyway, that's the background on why you're seeing this. Your cmd presumably ended with a function that returned an UNSET!, like maybe print?
Here's an example that may be illustrative:
exec-cmd: func [
cmd [block!] "Rebol instructions"
/local err
] [
set/any 'result (try [do cmd])
case [
unset? get/any 'result [
print "Cmd returned no result"
]
function? :result [
print ["Cmd returned a function:" newline (mold :result)]
]
;-- Rebol3 only --
;
; closure? :result [
; print ["Cmd returned a closure:" newline (mold :result)]
; ]
;-- Rebol3 should be changed to match Red and not require this --
;
lit-word? :result [
print ["Cmd returned a literal word:" newline (mold :result)]
]
error? result [
print mold disarm result
]
true [
print ["Cmd returned result of type" (mold type? result)]
print ["The value was:" newline (mold result)]
]
]
]
Notice that once you've already handled the case where result might be unset, you don't have to use get/any and can just do normal access.
There is a foundational issue in the way the interpreter works, that if a word is bound to a FUNCTION! value (also the CLOSURE! values in Rebol3) then referencing that word invokes the related code. To work around this, if you know you're in a situation where a word may hold a such a value you can use GET or the analogue of a SET-WORD! known as a GET-WORD!. These are generally considered "ugly" so it's best if you can isolate the part of the code that needs to test for such an edge case and not wind up putting colons in front of things you don't need to!
What has been deemed a design flaw is something called "lit-word decay". This necessitates the use of a GET-WORD! in Rebol2 if you have an actual literal word in your hand. In that case, your program won't crash, it just won't give you what you expect. It's explained here...it has already been changed in Red so it's certain to change for Rebol3.
Also, the concept of errors being "armed" and needing to be "disarmed" to be inspected has been eliminated in Rebol3. That doesn't affect the error? test in Rebol2 such that you'd need to use a GET-WORD!, but affected just about everything else you could do with them.
All right. I think I've covered all the cases here, but someone will correct me if I haven't!
(Note: if you're curious how to make your own function that returns an UNSET! like print does, just use exit instead of return)
>> nothing: func [value] [exit]
>> type? nothing 1020
== unset!
Use set/any and get/any to handle values that regular assignment and evaluation can't.
if error? set/any 'err try [
do cmd
] [ print mold disarm get/any 'err ]
Once the error is disarmed you can handle it normally.
rebol []
secretAgent: do func[ /local person firstName lastName][
firstName: "James"
lastName: "Bond"
person: make object! [
whoAreYou: func[][
print rejoin ["My name is " lastName ", " firstName " " lastName]
]
]
]
secretAgent/whoAreYou
if (error? (error: try [secretAgent/firstName])) [
probe disarm error
]
input
returns
My name is Bond, James Bond
** Script Error: Invalid path value: firstName
** Near: secretAgent/firstName
whereas I would expect same result as for
probe disarm try [secretAgent/firstName]
input
which returns:
My name is Bond, James Bond
make object! [
code: 311
type: 'script
id: 'invalid-path
arg1: 'firstName
arg2: none
arg3: none
near: [secretAgent/firstName]
where: none
]
Ah, that's a good example of why R3 tones down the way errors get triggered.
In R2, when an ERROR! values is evaluated (processed by the interpreter) it will activate the error handling mechanism. So, if you're not really careful, as you pass around the error value (like passing it to a function, returning it as a result, or in your case, evaluating it within a paren expression), it's going to trigger the error exception handler again.
In retrospect, this hair trigger was a poor evaluation rule. So, that's why R3 no longer handles errors that way. But, we cannot change it in R2.
Try without the extra (parentheses)
if error? error: try [secretAgent/firstName] [
probe disarm error
]
REBOL 2 errors are hair trigger. Your error was triggered in bubbling up one set of parentheses, rather than trapped.
See the difference here:
if error? error: try [0 / 0] [print ['bad mold disarm error]]
if error? (error: try [0 / 0]) [print ['bad mold disarm error]]
REBOL 3 error handling is slightly different -- disarm is not longer necessary, for example.
When typing in rebol console
do read http://askcodegeneration.com/csharp/simple-class/
I get get-access-modifier called twice:
Access modifier:
1. private: member can be accessed only by code in the same class
2. protected: member can be accessed only by code in the same class or in a derived class
3. internal: member can be accessed only by code in the same assembly
4. public: member can be accessed by code in the same assembly or another assembly that references it choice (by default 1):
Access modifier:
1. private: member can be accessed only by code in the same class
2. protected: member can be accessed only by code in the same class or in a derived class
3. internal: member can be accessed only by code in the same assembly
4. public: member can be accessed by code in the same assembly or another assembly that references it choice (by default 1):
Whereas it is only mentioned once in the source code:
append fields-template-output form reduce [
(to-word get-access-modifier) field-layout
]
I really can't see why, can you ?
Original code here (Internet Archive)
Yes. There is only one call to it, but it's inside of a foreach. Your default is two fields, so you get asked twice. Enter more, you'll get asked more.
While you could (and probably should) do the obvious thing of saving it in a variable, Rebol has other ways. For instance you could compose the block of code:
foreach field-layout fields-layout COMPOSE/DEEP [
append fields-template-output " "
append fields-template-output form reduce [
to-word (get-access-modifier) field-layout
]
append fields-template-output ";"
append fields-template-output newline
]
The composition runs once, looks deep for the parentheses in the block, and evaluates the code. (Kind of how parse does when it sees parentheses). The rest is left alone. So the block with the substitutions done is what's passed into FOREACH to run the loop.
Just a nuance of how you could have a call that appears to be inside a loop and yet is executed only once. I wouldn't suggest using it for something like this.
What I would suggest is studying making things less redundant in your code, by learning some more Rebol primitives like REJOIN...which builds a series out of a block. The series type will match whatever the first type it sees is (or a string if the first element is not a series):
modifier: get-access-modifier ;-- called only once, stored in variable
foreach field-layout fields-layout [
append fields-template-output rejoin [
" "
(to-string modifier)
field-layout
";"
newline
]
]
To solve the problem I have used static var to detect that it is executed only once (thanks to Sunanda tips is it possible to have static variable inside a rebol function? ).
ask-params: function [config-file [file!] default-class-name default-fields] [
;config-file: %askcodegeneration.com/csharp/simple-class/simple-class.params.txt
either value? 'class-name [
ans: ask rejoin ["class name" " (by default " class-name ")" ": "]
if ans <> "" [class-name: ans]
][
class-name: default-class-name
ans: ask rejoin [{class name (default "} class-name {"): }]
if ans <> "" [class-name: ans]
]
either exists? it: config-file [
params-block: copy load it
][
params-block: []
]
either res: find params-block class-name [
fields: pick res 2
print [ class-name "found in" it]
][
fields: default-fields
ans: ask rejoin [{fields (by default } {"} fields {"} {): }]
if ans <> "" [fields: ans]
new-param: reduce [
mold class-name
mold fields
]
either not exists? config-file [
create-directory "askcodegeneration.com/csharp/simple-class/"
write/append config-file reform new-param
][
write/append config-file reform [newline new-param]
]
]
append ret-value: [] class-name
append ret-value fields
ret-value
]