How to get values from the Red/System parts in a Red file - red

I am using the Red binding to read and write files, and the hard-coded-file-names version works well. But I want to get file names from the command line dynamically. As Red has no such utility right now. So I try to make it with Red/System. I can get command line args now, but I don't know how to pass it to the Red part. Like the example below, I need to pass source-file and target-file to read and write:
Red []
#include %input-output.red
#system-global [
args: system/args-list
args: args + 1
source-file: args/item
args: args + 1
target-file: args/item
print [source-file target-file ]
]
data: read source-file
probe data
write target-file data

Something like this should work, just convert your #system-global code to a routine function:
Red [
File: "test-read.red"
]
read-arg: routine [
files [block!]
/local
str [red-string!]
args [str-array!]
][
args: system/args-list
args: args + 1
str: string/load symbol/duplicate args/item 1 + length? args/item UTF-8
block/rs-append files as red-value! str
args: args + 1
str: string/load symbol/duplicate args/item 1 + length? args/item UTF-8
block/rs-append files as red-value! str
]
probe read-arg []
Then once compiled, you should get this result:
C:\Dev\Red>test-read fileA fileB
["fileA" "fileB"]

Presuming that the arguments to the read and write functions are string!, you will need to write a routine that will return a red-string! of a Red/System c-string!.
The Red API is understandably not documented yet as Nenad is busy working on getting to Red 1.0. This is a function which goes the other way, string! to c-string! that might help show some light on what is needed - https://github.com/PeterWAWood/Red-System-Libs/blob/master/UTF8/string-c-string.reds .
There may be an easer way to use the Red api that somebody else can suggest.

Related

Several inputs from a single input window

Out of curiosity, I was trying to find a way to combine several inputs into a single input window (currently set to string) on the interface. In the current example, these inputs are the 4 parameters necessary for resize-world.
While my result works, I would be interested to hear if someone else knows a more elegant solution. I specifically dislike the use of item here.
Interface:
input-window
[0 5 0 5]
Code:
to setup
let worldsizes runresult input-window
resize-world item 0 worldsizes item 1 worldsizes item 2 worldsizes item 3 worldsizes
end
Using this post as base, I created a procedure that allows a command, both anonymous and regular, to take a single list of arguments as input.
Interface:
input-window
[0 5 0 5]
Code:
to test
let worldsize run-result input-window ;creates a list
apply-command "resize-world" worldsize
end
to apply-command [command inputlist]
; Takes two inputs
; 1: Anonymous command/string containing anonymous command/string containing regular command
; 2: A list of inputs, corresponding in length to the number of inputs the command takes
; Applies the command with the different items of the list as input
; Heavily inspired by stackoverflow-user Bryan head
set command anonimify-command command length inputlist ; Using non-anonymous commands gave issues, hence they are anonimified
(run listify-command command length inputlist inputlist)
end
to-report listify-command [ command num-args ]
; Takes an anonymous command and the length of a list as input
; Outputs an anonymous command that takes the different items of the list as inputs
; Heavily inspired by stackoverflow-user Bryan head
if (not is-anonymous-command? command) [error "The first input for listify-command has to be an anonymous command"]
let args (reduce word n-values num-args [ x -> (word " item " x " ?") ])
;show (word runresult (word "[ ? -> (run command " args ") ]"))
report runresult (word "[ ? -> (run command " args ") ]")
end
to-report anonimify-command [ command number-of-inputs]
; Takes two inputs
; 1: Takes an anonymous command, a string containing an anonymous command or a string containing a regular command
; 2: Takes a number that corresponds to the number of inputs the anonimified command should take
; Returns anonymous commands unaltered
; Returns strings containing an anonymous command as an anonymous command
; Returns strings containing a regular command as an anonymous command
if (is-anonymous-command? command) [ ; Anonymous commands get returned
report command
]
if (is-string? command) [ ; Strings are processed
carefully [ ; Using run-result on a string that does not contain an anonymous command causes an error, hence carefully
if (is-anonymous-command? run-result command) [
set command run-result command
]
]
[
let inputs n-values number-of-inputs [ i -> word " x" i]
let inputs-as-string reduce word inputs
let command-string (word "[ [" inputs-as-string " ] -> " command inputs-as-string" ]")
set command run-result command-string
]
report command
]
error "The inputted command must be either an anonymous command, a string containing an anonymous command or a string containing a command"
;If the input is neither anonymous command nor string, an error is displayed
end

Netlogo - read and import string data from txt file

I am trying to read a .txt file containing strings:
Delivery LHR 2018
Delivery LHR 2016
Delivery LHR 2014
Delivery LHR 2011
Delivery LHR 2019
Delivery LHR 1998
I have tried below codes but not working. It reported "expect a literal value" when running file-read
globals [input]
to setup
set input []
file-open "test.txt"
while [not file-at-end?]
[
let a quote file-read
let b quote file-read
set input lput a input
set input lput b input
print input
]
file-close
end
to-report quote [ #thing ]
ifelse is-number? #thing
[ report #thing ]
[ report (word "\"" #thing "\"") ]
end
You can kind-of get what you want with the csv extension which comes with NetLogo. It at least let's you specify a delimiter, so " ", but you'll have to manually read past all the blank columns it'll see.
extensions [csv]
globals [input]
to setup
set input []
let lines (csv:from-file "test.txt" " ")
foreach lines [ line ->
let col1 (item 0 line)
let i 1
while [item i line = ""] [ set i (i + 1) ]
let col2 (item i line)
show col2
set i (i + 1)
while [item i line = ""] [ set i (i + 1) ]
let col3 (item i line)
show col3
set input lput col1 input
]
show input
end
The reason it doesn`t work can be found in the file-read description from the NetLogo Dictionary Manual (https://ccl.northwestern.edu/netlogo/docs/dictionary.html#file-read)
[...]Note that strings need to have quotes around them.[...]
It is not a solution to add the quotes within NetLogo because file-read already throws an Error, if the next entry in the file is not one of number, list, string, boolean, or the special value nobody. And string in this case means, it needs to have quotes around it.
Thus, to read the file into NetLogo you have to put quotes around the strings in your input file. Alternatively, if the strings in your input file always have the same length, you could try to read the file using the primitive file-read-characters. Here is an example that should work with your input file:
to setup
file-open "test.txt"
while [not file-at-end?]
[
let a file-read-characters 8
let skip file-read-characters 4
let b file-read-characters 3
let c file-read
print (list a b c)
]
file-close
end

Easiest way to ignore blank lines when reading a file in Netlogo

I have some code that reads a file of names and creates a list:
let who-file-name "world-health-field-surveillance.csv"
let who-file-name-dict csv:from-file who-file-name
let who-file-names sort [who] of names
let index 1 ;not 0, this removes the header in the csv
repeat length who-file-names [
file-open "world-health-field-surveillance.csv"
if file-at-end? [stop]
let entry (item 0 (item index who-file-name-dict))
if entry = "\n" [stop]
The file might end with some blank lines or its possible the file has names separated by a newline, like so:
Allman
Atkinson
Behlendorf
I want to ignore any lines that contain only whitespace.
My sample code doesn't work.
How could I do this in netlogo?
What are you trying to do, exactly? If I have a csv file that looks like this:
If I run this code:
extensions [ csv ]
to setup
ca
let example csv:from-file "example_names.csv"
print example
reset-ticks
end
I get a list output that looks like:
[[Allman] [Atkinson] [Behlendorf] [Belnich] [Cravit] [Court]]
Is that not what you're after? If you need just a single-level list, you can do
print reduce sentence example
to get
[Allman Atkinson Behlendorf Belnich Cravit Court]

ipython: SMILES parse error

In shell:
balloon/balloon -f balloon/MMFF94.mff --nconfs 1 --nGenerations 300 "[H]OC(=O)C([H])([H])[C##]1([H])C2=C([H])C([H])=C(OC([H])([H])C([H])([H])C([H])([H])OC3=C(OC([H])([H])[H])C([H])=C(C([H])=C3[H])C3=NC(OC([H])([H])C([H])([H])[H])=C(S3)C([H])([H])[H])C([H])=C2C([H])([H])C1([H])[H]" AAA.pdb
It works very well. However, when I try to implement use ipython, there is an error, my code as follows:
import os
str3="[H]OC(=O)C([H])([H])[C##]1([H])C2=C([H])C([H])=C(OC([H])([H])C([H]([H])C([H([H])OC3=C(OC([H])([H])[H])C([H])=C(C([H])=C3[H])C3=NC(OC([H])([H])C([H])([H][H])=C(S3)C([H])([H])[H])C([H])=C2C([H])([H])C1([H])[H]"
str4="balloon/balloon -f balloon/MMFF94.mff --nconfs 1 --nGenerations 300 str3 AAC.pdb"
#os.system('balloon_options')
os.system(str4)
The error is:
Cannot parse input str3 SMILES parser said: Syntax error after s
Skipping.
What is wrong with the problem?
By the way, balloon is the software which can put SMILES STRING into mol2 or pdb format.(SMILES STRING is like [H]OC(=O)C([H])([H])[C##]1([H])C2=C([H])C([H])=C(OC([H])([H])C(HC([H([H])OC3=C(OC([H])([H])[H])C([H])=C(C([H])=C3[H])C3=NC(OC([H])([H])C([H])([H][H])=C(S3)C([H])([H])[H])C([H])=C2C([H])([H])C1([H])[H])
You want to give the contents of the string str3 as an argument to balloon/balloon, but instead you're giving the string "str3" as the argument, and the string "str3" isn't valid SMILES.
Try using this line:
str4="balloon/balloon -f balloon/MMFF94.mff --nconfs 1 --nGenerations 300 " + str3 + " AAC.pdb"

How to use Unicode codepoints above U+FFFF in Rebol 3 strings like in Rebol 2?

I know you can't use caret style escaping in strings for codepoints bigger than ^(FF) in Rebol 2, because it doesn't know anything about Unicode. So this doesn't generate anything good, it looks messed up:
print {Q: What does a Zen master's {Cow} Say? A: "^(03BC)"!}
Yet the code works in Rebol 3 and prints out:
Q: What does a Zen master's {Cow} Say? A: "μ"!
That's great, but R3 maxes out its ability to hold a character in a string at all at U+FFFF apparently:
>> type? "^(FFFF)"
== string!
>> type? "^(010000)"
** Syntax error: invalid "string" -- {"^^(010000)"}
** Near: (line 1) type? "^(010000)"
The situation is a lot better than the random behavior of Rebol 2 when it met codepoints it didn't know about. However, there used to be a workaround in Rebol for storing strings if you knew how to do your own UTF-8 encoding (or got your strings by way of loading source code off disk). You could just assemble them from individual characters.
So the UTF-8 encoding of U+010000 is #F0908080, and you could before say:
workaround: rejoin [#"^(F0)" #"^(90)" #"^(80)" #"^(80)"]
And you'd get a string with that single codepoint encoded using UTF-8, that you could save to disk in code blocks and read back in again. Is there any similar trick in R3?
There is a workaround using the string! datatype as well. You cannot use UTF-8 in that case, but you can use UTF-16 workaround as follows:
utf-16: "^(d800)^(dc00)"
, which encodes the ^(10000) code point using UTF-16 surrogate pair. In general, the following function can do the encoding:
utf-16: func [
code [integer!]
/local low high
] [
case [
code < 0 [do make error! "invalid code"]
code < 65536 [append copy "" to char! code]
code < 1114112 [
code: code - 65536
low: code and 1023
high: code - low / 1024
append append copy "" to char! high + 55296 to char! low + 56320
]
'else [do make error! "invalid code"]
]
]
Yes, there is a trick...which is the trick you should have been using in R2 as well. Don't use a string! Use a binary! if you have to do this sort of thing:
good-workaround: #{F0908080}
It would've worked in Rebol2, and it works in Rebol3. You can save it and load it without any funny business.
In fact, if care about Unicode at all, ever...stop doing string processing that is using codepoints higher than ^(7F) if you are stuck in Rebol 2 and not 3. We'll see why by looking at that terrible workaround:
terrible-workaround: rejoin [#"^(F0)" #"^(90)" #"^(80)" #"^(80)"]
..."And you'd get a string with that single UTF-8 codepoint"...
The only thing you should get is a string with four individual character codepoints, and with 4 = length? terrible-workaround. Rebol2 is broken because string! is basically no different from binary! under the hood. In fact, in Rebol2 you could alias the two types back and forth without making a copy, look up AS-BINARY and AS-STRING. (This is impossible in Rebol3 because they really are fundamentally different, so don't get attached to the feature!)
It's somewhat deceptive to see these strings reporting a length of 4, and there's a false comfort of each character producing the same value if you convert them to integer!. Because if you ever write them out to a file or port somewhere, and they need to be encoded, you'll get bitten. Note this in Rebol2:
>> to integer! #"^(80)"
== 128
>> to binary! #"^(80)"
== #{80}
But in R3, you have a UTF-8 encoding when binary conversion is needed:
>> to integer! #"^(80)"
== 128
>> to binary! #"^(80)"
== #{C280}
So you will be in for a surprise when your seemingly-working code does something different at a later time, and winds up serializing differently. In fact, if you want to know how "messed up" R2 is in this regard, look at why you got a weird symbol for your "mu". In R2:
>> to binary! #"^(03BC)"
== #{BC}
It just threw the "03" away. :-/
So if you need for some reason to work with a Unicode strings and can't switch to R3, try something like this for the cow example:
mu-utf8: #{03BC}
utf8: rejoin [#{} {Q: What does a Zen master's {Cow} Say? A: "} mu-utf8 {"!}]
That gets you a binary. Only convert it to string for debug output, and be ready to see gibberish. But it is the right thing to do if you're stuck in Rebol2.
And to reiterate the answer: it's also what to do if for some odd reason stuck needing to use those higher codepoints in Rebol3:
utf8: rejoin [#{} {Q: What did the Mycenaean's {Cow} Say? A: "} #{010000} {"!}]
I'm sure that would be a very funny joke if I knew what LINEAR B SYLLABLE B008 A was. Which leads me to say that most likely, if you're doing something this esoteric you probably only have a few codepoints being cited as examples. You can hold most of your data as string up until you need to slot them in conveniently, and hold the result in a binary series.
UPDATE: If one hits this problem, here is a utility function that can be useful for working around it temporarily:
safe-r2-char: charset [#"^(00)" - #"^(7F)"]
unsafe-r2-char: charset [#"^(80)" - #"^(FF)"]
hex-digit: charset [#"0" - #"9" #"A" - #"F" #"a" - #"f"]
r2-string-to-binary: func [
str [string!] /string /unescape /unsafe
/local result s e escape-rule unsafe-rule safe-rule rule
] [
result: copy either string [{}] [#{}]
escape-rule: [
"^^(" s: 2 hex-digit e: ")" (
append result debase/base copy/part s e 16
)
]
unsafe-rule: [
s: unsafe-r2-char (
append result to integer! first s
)
]
safe-rule: [
s: safe-r2-char (append result first s)
]
rule: compose/deep [
any [
(either unescape [[escape-rule |]] [])
safe-rule
(either unsafe [[| unsafe-rule]] [])
]
]
unless parse/all str rule [
print "Unsafe codepoints found in string! by r2-string-to-binary"
print "See http://stackoverflow.com/questions/15077974/"
print mold str
throw "Bad codepoint found by r2-string-to-binary"
]
result
]
If you use this instead of a to binary! conversion, you will get the consistent behavior in both Rebol2 and Rebol3. (It effectively implements a solution for terrible-workaround style strings.)