When an input stream containing audio data is opened, the following slots will be set:
audioChannels audioSampleRate audioBitRate audioDuration audioFrameCountWhen an input stream containing video data is opened, the following slots will be set:
framePeriod videoDuration videoFrameCount
if(streamDestination, streamDestination write(outputBuffer)) outputBuffer empty
sensors = AppleSensors clone value := sensors getRightLightSensor
Note: This addon is only needed for async file request - all socket ops are already asynchronous in Io.
bf = Blowfish clone bf setKey("secret") bf beginProcessing bf inputBuffer appendSeq("this is a message") bf process bf endProcess bf outputBuffer // this contains the encrypted data
#!./ioServer cgi = CGI clone redirect = cgi getParameters at("redirurl") if (redirect and redirect != "", redirect clipAfterStartOfSeq("\r") redirect clipAfterStartOfSeq("\n") cgi redirect(redirect) System exit(0) ) cgi header("Content-type", "text/html") cgi write("<html><head><title>test</title><body>") cgi write("GET Parameters:") cgi getParameters foreach(k, v, cgi write(k .. " = " .. v .. ",")) ) cgi write("POST Parameters:") cgi postParameters foreach(k, v, cgi write(k .. " = " .. v .. ",")) ) cgi write("COOKIES:") cgi cookies foreach(k, v, cgi write(k .. " = " .. v .. ",") )
fileName content (raw content of file as Sequence) contentType contentEncoding size (in characters/bytes) asString (pretty string of name, type, size)
Every N number of object allocs, the collector will walk some of the objects marked as gray, marking their connected white objects as gray and turning themselves black. Every M allocs, it will pause for a sweep where it makes sure all grays are marked black and io_frees all whites.
If the sweepsPerGeneration is set to zero, it will immediately mark all blacks as white again and mark the root objects as gray. Otherwise, it will wait until the sweepsPerGeneration count is reached to do this. By adjusting the allocsPerSweep and sweepsPerGeneration appropriately, the collector can be tuned efficiently for various usage cases. Generally, the more objects in your heap, the larger you'll want this number.
messageForString(aString, optionalLabelString)
Curses init Curses move(5, 7) print(\"Hello\") Curses refresh Curses end
Person := DBIRecord clone do (fullName := method(firstName.." "..lastName)) q := conn query("SELECT id, firstName, lastName FROM people") q foreach(Person, p, writeln("Name = ", p fullName))As you can see, fullName was not in the SQL query, however, a dynamic method in your Person class. DBIRecord in and of itself provides no real functionality. It simply acts as an Object and stores the values from the SQL query into a Map. You can access the field information:
o := r populate(Person) o firstName // would retrieve the firstName value of the SQL query o setFirstName("John") // would update the object's firstName value to be JohnDo not confuse the above example as updating the actual database. The call to setFirstName only updates the objects representation of firstName.
r := conn query("SELECT * FROM people") r foreach(r, r at(1))The above would start at the first row, however, you can move around in the result set and then foreach would pickup where you left off, for instance, say you wanted to skip the first three rows:
r := conn query("SELECT * FROM people") r seek(4) r foreach(r, r at (1))The above would start at the record #4, not at the beginning. The optional Object parameter would cause a decendent of DBIRecord to be populate instead of the index being set. This allows for advanced functionality. Please see `DBIRecord' for further information and an example.
dnsQueryPacketForHostName(hostNameSeq)
addCoro(aCoro)
DNSResolver addDNSServerIp("128.105.2.10") ipForYahoo := DNSResolver ipForHostName("yahoo.com")
con := DOConnection clone setHost("127.0.0.1") setPort(8456) connect result := con serverObject test(1) writeln(result) r := result at(0) writeln(r) r := result at(1) writeln(r)Implementation Notes:
The format of the Distributed Objects message is a list of NullCharacter terminated strings in one of these two formats:
Send message format:
s NullCharacter targetId NullCharacter messageName NullCharacter argCount NullCharacter argType NullCharacter argValue NullCharacter (next arg type and value, etc)Reply message format:
r NullCharacter argType NullCharacter argvalue NullCharacterIf the argument is not a String, Number or nil then: If it is local to the sender, the type is RemoteObject. If it is a proxy to a remote object, the type is LocalObject. This isn't optimized yet.
Test := Object clone Test test := method(v, write("got test '", v, "'\n") return List clone append(1) ) doServer := DOServer clone doServer setRootObject(Test clone) doServer setPort(8456) doServer start
%a abbreviated weekday name (Sun, Mon, etc.) %A full weekday name (Sunday, Monday, etc.) %b abbreviated month name (Jan, Feb, etc.) %B full month name (January, February, etc.) %c full date and time string %d day of the month as two-digit decimal integer (01-31) %H hour as two-digit 24-hour clock decimal integer (00-23) %I hour as two-digit 12-hour clock decimal integer (01-12) %m month as a two-digit decimal integer (01-12) %M minute as a two-digit decimal integer (00-59) %p either "AM" or "PM" %S second as a two-digit decimal integer (00-59) %U number of week in the year as two-digit decimal integer (00-52) with Sunday considered as first day of the week %w weekday as one-digit decimal integer (0-6) with Sunday as 0 %W number of week in the year as two-digit decimal integer (00-52) with Monday considered as first day of the week %x full date string (no time); in the C locale, this is equivalent to "%m/%d/%y". %y year without century as two-digit decimal number (00-99) %Y year with century as four-digit decimal number %Z time zone name (e.g. EST); null string if no time zone can be obtained %% stands for '%' character in output string.
Coroutine currentCoroutine setMessageDebugging(true)
Then each message sent within that coroutine will cause the Debugger
vmWillSendMessage slot to be activated and the Debugger slots:
messageCoroutine, messageSelf, messageLocals, and message will be set with the
values related to the current message send. You can override vmWillSendMessage to
implement your own debugging mechanisms.
%y years without century as two-digit decimal number (00-99) %Y year with century as four-digit decimal number %d days %H hour as two-digit 24-hour clock decimal integer (00-23) %M minute as a two-digit decimal integer (00-59) %S second as a two-digit decimal integer (00-59) The default format is "%Y %d %H:%M:%S".
call(functionName, <arg1>, <arg2>, ...)
file = File clone openForUpdating("/tmp/test") lines = file readLines reverse file rewind lines foreach(i, line, file write(line, "\n")) file close
aFile foreach(i, v, writeln("byte at ", i, " is ", v)) aFile foreach(v, writeln("byte ", v))
aFile foreachLine(i, v, writeln("Line ", i, ": ", v)) aFile foreach(v, writeln("Line: ", v))
If not set, no special restrictions are placed on matching a period.
// within a GLUT display callback... timesFont = Font clone open(\"times.ttf\") if (timesFont error, write(\"Error loading font: \", timesFont error, \"\n\"); return) timesFont setPointSize(16) glColor(0,0,0,1) timesFont draw(\"This is a test.\")Rendering fonts using OpenGL textures
Smaller fonts (those having a point size around 30 or smaller, depending on the font) will automatically be cached in and rendered from a texture. This technique is very fast and should support rendering speeds as fast (or faster than) those of typical desktop font rendering systems. Larger font sizes(due to texture memory constraints) will be rendered to a pixelmap when displayed. Thanks to Mike Austin for implementing the font texturing system.
Note; Fonts are draw as RGBA pixel maps. These blending options are recommended:
glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
image = Image clone open("curly.png") image draw image scaleTo(image width / 2, image height / 2) image save("curly.tiff")When loading an attempt will be made to convert the image into whichever of the following formats it is closest to: L8, LA8, RGB8, RGBA8.
Currently supported formats include PNG(which supports alpha), JPG and TIFF.
bf = LZO clone bf beginProcessing bf inputBuffer appendSeq("this is a message") bf process bf endProcess bf outputBuffer // this contains the encoded data
bf = LZO clone bf beginProcessing bf inputBuffer appendSeq("this is a message") bf process bf endProcess bf outputBuffer // this contains the encoded data
list(1, 2, 3, 4) detect(i, v, v > 2)
==> 3
list(1, 2, 3, 4) detect(v, v > 2)
==> 3
list(1,2,list(3,4,list(5))) flatten
==> list(1, 2, 3, 4, 5)
list(1, 2, 3) foreach(i, v, writeln(i, " = ", v)) list(1, 2, 3) foreach(v, writeln(v))
list(1, 5, 7, 2) select(i, v, v > 3) print
==> 5, 7
list(1, 5, 7, 2) select(v, v > 3) print
==> 5, 7
aMap foreach(k, v, writeln(k, " = ", v)) aMap foreach(v, write(v))Example use with a block:
myBlock = block(k, v, write(k, " = ", v, "\n")) aMap foreach(k, v, myBlock(k, v))
Terminology
Example; A B(C D); E F In the above example: ...Important; Modifying the message tree of a block currently in use may cause a crash if a garbage collection cycle occurs. If the implementation were changed to retain every called message, this could be avoided. But the cost to performance seems to outweigh the need to cover this case for now.
Io> message(a) appendArg(message(b)) ==> a(b) Io> message(a(1,2)) appendArg(message(3)) ==> a(1, 2, 3)
Io> message(a(1,2,3)) argCount ==> 3 Io> message(a) argCount ==> 0
1234.5678 asString(0, 2)would return:
1234.57
Io> 1 years
==> 1 years 00 days 00:00:0.000000
Io> 20 years
==> 20 years 00 days 00:00:0.000000
With this, you can do things such as:
Io> Date clone now + 5 years
==> 2011-11-14 18:44:33 EST
Io> Date clone now + 2 years + 3 days + 22 minutes
==> 2008-11-17 19:06:54 EST
MyObject test // performs test
MyObject ?test // performs test if MyObject has a slot named test
The search for the slot only follows the receivers proto chain.
myObject foreach(n, v, writeln("slot ", n, " = ", v type) ) myObject foreach(v, writeln("slot type ", v type) )
Example:
myProxy forward = method( messageName := thisMessage name arguments := thisMessage arguments myObject doMessage(thisMessage) )
Dog := Mammal clone do(
init := method(
resend
)
)
calling Dog init will send an init method to Mammal, but using the Dog's context.
self test(1, 2) // performs test(1, 2) on self
super(test(1, 2)) // performs test(1, 2) on self proto but with the context of self
hour := Date hour switch(
12, "midday",
0, "midnight",
17, "teatime",
Date hour asString
)
Io> a := method(x, x = x + 1; if(x > 10, return x); tailCall(x)) ==> method(x, updateSlot("x", x +(1)); if(x >(10), return(x)); tailCall(x)) Io> a(1) ==> 11
// First method (operating on numbers) 1 to(10) foreach("iterating" print) // prints "iterating" 10 times // Second method (operating on numbers) 1 to(10) foreach(v, v print) // prints each value // Third method (operating on numbers) 1 to(10) foreach(i, v, writeln(i .. ": " .. v)) // prints "index: value"
The Regex addon adds support for Perl regular expressions using the PCRE library by Philip Hazel.
Io> re := "is.*a" asRegex Io> "This is a test. This is also a test." allMatchesOfRegex("is.*a") replaceAllWith("is not a") ==> "This is not a test. This is not a test.
Io> "11aabb" allMatchesOfRegex("aa*") ==> list("a", "a") Io> re := "(wom)(bat)" asRegex Io> "wombats are cuddly" matchesOfRegex(re) replaceAllWith("$2$1!") ==> batwom!s are cuddly
Io> "WORD" matchesRegex("[a-z]+") ==> false Io> "WORD" matchesRegex("[a-z]+" asRegex caseless) ==> true
Returns a clone of the receiver with the dotall option turned on, or self if the receiver itself has the option turned on.
In dotall mode, "." matches any character, including newline. By default it matches any character except newline.
Io> "A\nB" matchesOfRegex(".+") next string ==> A Io> "A\nB" matchesOfRegex(".+" asRegex dotAll) next string ==> A\nB
Returns a clone of the receiver with the extended option turned on, or self if the receiver itself has the option turned on.
In extended mode, a Regex ignores any whitespace character in the pattern except when escaped or inside a character class. This allows you to write clearer patterns that may be broken up into several lines.
Additionally, you can put comments in the pattern. A comment starts with a "#" character and continues to the end of the line, unless the "#" is escaped or is inside a character class.
Returns a clone of the receiver with the multiline option turned on, or self if the receiver itself has the option turned on.
In multiline mode, "^" matches at the beginning of the string and at the beginning of each line; and "$" matches at the end of the string, and at the end of each line. By default "^" only matches at the beginning of the string, and "$" only matches at the end of the string.
Io> "A\nB\nC" allMatchesForRegex("^.") ==> list("A") Io> "A\nB\nC" allMatchesForRegex("^." asRegex multiline) ==> list("A", "B", "C")
Io> match := "37signals" findRegex("([0-9]+)([a-z]+)(!!)?") ==> RegexMatch: "37signals" # Item 0 is the entire matched string: Io> match at(0) ==> 37signals # Item 1 is the first capture ("[0-9]+"): Io> match at(1) ==> 37 # Item 2 is the second capture ("[a-z]+"): Io> match at(2) ==> signals # The third sub pattern wasn't part of the match, so item 3 is nil: Io> match at(3) ==> nil # You can access captures by name: Io> match at("number") ==> 37 Io> match at("word") ==> signals
at(0)
is the entire match.
$0
is replaced with the whole match, $1
is replaced with the first
sub capture, etc. ${name}
is replaced with the capture of that name.
foreach
, but the result of each evaluation of message is returned in a list.
match subject slice(match end)
match subject slice(0, match start)
foreach
, but the values for which the result of evaluating message are non-nil are returned
in a list.
next
, but will only match at the current search position.
foreach
, but takes an extra message that will be evaluated for the non-matching
text before each match, and the non-matching text after the last match.
foreach
, but the result of each evaluation of message is returned
in a list.
replace(match, match expandTo(templateString))
Io> "funkadelic" matchesOfRegex("\\w+") setEndPosition(4) next string ==> funk Io> "funkadelic" matchesOfRegex("\\w+") setEndPosition(nil) next string ==> funkadelic
startElement(name) endElement(name) newAttribute(key, value) newText(text)for each of the items it finds. Returns self.
db := SQLite clone db setPath("myDatabase.sqlite") db open db exec("CREATE TABLE Dbm (key, value)") db exec("CREATE INDEX DbmIndex ON Dbm (key)") db exec("INSERT INTO Dbm ('key', 'value') VALUES ('a', '123')") db exec("INSERT INTO Dbm ('key', 'value') VALUES ('a', 'efg')") rows := db exec("SELECT key, value FROM Dbm WHERE key='a'") db exec("DELETE FROM Dbm WHERE key='a'") rows := db exec("SELECT key, value FROM Dbm WHERE key='a'") db close
db := SQLite clone db setPath("myDatabase.sqlite") db open db exec("CREATE TABLE Dbm (key, value)") db exec("CREATE INDEX DbmIndex ON Dbm (key)") db exec("INSERT INTO Dbm ('key', 'value') VALUES ('a', '123')") db exec("INSERT INTO Dbm ('key', 'value') VALUES ('a', 'efg')") rows := db exec("SELECT key, value FROM Dbm WHERE key='a'") db exec("DELETE FROM Dbm WHERE key='a'") rows := db exec("SELECT key, value FROM Dbm WHERE key='a'") db close
Terminology
Io> "abc" alignCenter(10, "-")
==> ---abc----
Io> "abc" alignCenter(10, "-=")
==> -=-abc-=-=
Io> "abc" alignLeft(10, "+")
==> abc+++++++
Io> "abc" alignLeft(10, "-=")
==> abc-=-=-=-
Io> "abc" alignRight(10, "-")
==> -------abc
Io> "abc" alignRight(10, "-=")
==> -=-=-=-abc
list(memberType1, memberName1, memberType2, memberName2, ...)
Member types include:
int8, int16, int32, int64 uint8, uint16, uint32, uint64 float32, float64Example:
pointObject := structPointSeq asStruct(list("float32", "x", "float32", "y"))The output pointObject would contain x and y slots with Number objects.
aSequence foreach(i, v, writeln("value at index ", i, " is ", v)) aSequence foreach(v, writeln("value ", v))
"Keep the tail" lstrip(" eKp") ==> "the tail"
"Cut the tail off" rstrip(" afilot") ==> "Cut the"
"abc" size == 3
"a b c d" splitNoEmpties => list("a", "b", "c", "d")
"a***b**c*d" splitNoEmpties("*") => list("a", "b", "c", "d")
"a***b||c,d" splitNoEmpties("*", "|", ",") => list("a", "b", "c", "d")
" Trim this string \r\n" strip ==> "Trim this string"
list(memberType1, memberName1, memberType2, memberName2, ...)
Member types include:
int8, int16, int32, int64 uint8, uint16, uint32, uint64 float32, float64Example:
pointStructSeq := Sequence withStruct(list("float32", 1.2, "float32", 3.5))The output pointStructSeq would contain 2 raw 32 bit floats.
Echo := Object clone Echo handleSocketFromServer := method(aSocket, aServer, write("[Got echo connection from ", aSocket host, "]\n") while(aSocket isOpen, if(aSocket read, aSocket write(aSocket readBuffer asString)) aSocket readBuffer empty ) write("[Closed ", aSocket host, "]\n") ) write("[Starting echo server on port 8456]\n") server := Server clone setPort(8456) server handleSocket := method(aSocket, Echo clone @handleSocketFromServer(aSocket, self) ) server startNotes
Io's use of lightweight threading and select for dealing with sockets makes for servers that are much more efficient(both memory and cpu wise) than those written with kernel threads and socket polling.
logger = Syslog clone do( identity("SyslogTest") facility(facilityMap at("LOG_USER")) options(List append(optionsMap at("LOG_PID"), optionsMap at("LOG_CONS"))) priority(priorityMap at("LOG_INFO")) open(facility, options) mask(List append(maskMap at("LOG_PRIMASK"))) log(priority, "*** Merely a test ***") close )
Note: This is partially tested. Please let me know of any problems you happen to stumble across, or if it could be better. --Jeremy Tregunna
Example:
options := System getOptions(args)
options foreach(k, v,
if(v type == List type,
v foreach(i, j, writeln(\"Got unnamed argument with value: \" .. j))
continue
)
writeln(\"Got option: \" .. k .. \" with value: \" .. v)
)
Thread createThread()
_open
bf = ZlibDecoder clone bf beginProcessing bf inputBuffer appendSeq(inputData) bf process bf endProcess bf outputBuffer // this contains the output data
bf = ZlibEncoder clone bf beginProcessing bf inputBuffer appendSeq("this is a message") bf process bf endProcess bf outputBuffer // this contains the result data