add existing documentation
This commit is contained in:
241
doc/example.asm
Executable file
241
doc/example.asm
Executable file
@@ -0,0 +1,241 @@
|
|||||||
|
@msgh (net.doorstuck.test.Person) [-init(name:age:)]
|
||||||
|
ldr x0, [bp, #-1]
|
||||||
|
str x0, [self, #0]
|
||||||
|
|
||||||
|
ldr x0, [bp, #-2]
|
||||||
|
str x0, [self, #1]
|
||||||
|
|
||||||
|
ret
|
||||||
|
@end
|
||||||
|
|
||||||
|
@msgh (net.doorstuck.test.Person) [-name]
|
||||||
|
ldr x0, [self, #0]
|
||||||
|
|
||||||
|
ret
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@msgh (net.doorstuck.test.Person) [-age]
|
||||||
|
ldr x0, [self, #1]
|
||||||
|
|
||||||
|
ret
|
||||||
|
@end
|
||||||
|
|
||||||
|
@msgh (net.doorstuck.test.Person) [-ageInMonths]
|
||||||
|
ldr x0, [self, #1]
|
||||||
|
ldr x1, #12
|
||||||
|
mul x0, x0, x1
|
||||||
|
|
||||||
|
ret
|
||||||
|
@end
|
||||||
|
|
||||||
|
@msgh (net.doorstuck.test.Person) [-setName:]
|
||||||
|
ldr x0, [bp, #-1]
|
||||||
|
str x0, [self, #0]
|
||||||
|
|
||||||
|
ret
|
||||||
|
@end
|
||||||
|
|
||||||
|
@msgh (net.doorstuck.test.Person) [-setAge:]
|
||||||
|
ldr x0, [bp, #-1]
|
||||||
|
str x0, [self, #1]
|
||||||
|
|
||||||
|
ret
|
||||||
|
@end
|
||||||
|
|
||||||
|
@msgh (net.doorstuck.test.Person) [-setAge:in:]
|
||||||
|
ldr x0, [bp, #-1]
|
||||||
|
|
||||||
|
ldr x1, "years"
|
||||||
|
cmp x0, x1
|
||||||
|
b.eq $L0001
|
||||||
|
|
||||||
|
ldr x1, "months"
|
||||||
|
cmp x0, x1
|
||||||
|
b.eq $L0002
|
||||||
|
|
||||||
|
ldr x1, "days"
|
||||||
|
cmp x0, x1
|
||||||
|
b.eq $L0003
|
||||||
|
|
||||||
|
; else
|
||||||
|
ldr x2, #0
|
||||||
|
str x2, [self, #1]
|
||||||
|
br $L0004
|
||||||
|
|
||||||
|
; if units == "years"
|
||||||
|
L0001: str x0, [self, #1]
|
||||||
|
br $L0004
|
||||||
|
|
||||||
|
; if units == "months"
|
||||||
|
L0002: ldr x2, #12
|
||||||
|
div x0, x0, x2
|
||||||
|
str x0, [self, #1]
|
||||||
|
br $L0004
|
||||||
|
|
||||||
|
; if units == "days"
|
||||||
|
L0003: ldr x2, #365
|
||||||
|
div x0, x0, x2
|
||||||
|
str x0, [self, #1]
|
||||||
|
|
||||||
|
; end
|
||||||
|
L0004:
|
||||||
|
ret
|
||||||
|
@end
|
||||||
|
|
||||||
|
@lambda (Lx0001) [_:]
|
||||||
|
ldr x1, @ident[cout]
|
||||||
|
ldr x2, [bp, #-1]
|
||||||
|
|
||||||
|
ldr x3, "Count is "
|
||||||
|
add x3, x3, x2
|
||||||
|
|
||||||
|
push x3
|
||||||
|
msg x1, @selector[-put:]
|
||||||
|
|
||||||
|
ret
|
||||||
|
@end
|
||||||
|
|
||||||
|
@lambda (Lx0002) []
|
||||||
|
ldr x1, @ident[cout]
|
||||||
|
|
||||||
|
ldr x2, "True!"
|
||||||
|
push x2
|
||||||
|
|
||||||
|
msg x1, @selector[-put:]
|
||||||
|
@end
|
||||||
|
|
||||||
|
@lambda (Lx0003) []
|
||||||
|
ldr x1, @ident[cout]
|
||||||
|
|
||||||
|
ldr x2, "False!"
|
||||||
|
push x2
|
||||||
|
|
||||||
|
msg x1, @selector[-put:]
|
||||||
|
@end
|
||||||
|
|
||||||
|
@init
|
||||||
|
rsv #3 ; 3 local variables
|
||||||
|
/* stack layout is now this:
|
||||||
|
bp[1] = p1
|
||||||
|
bp[2] = i
|
||||||
|
bp[3] = j
|
||||||
|
*/
|
||||||
|
|
||||||
|
; p1 = Person new(name:"John Doe", age:34)
|
||||||
|
ldr x0, #34
|
||||||
|
push x0
|
||||||
|
|
||||||
|
ldr x0, "John Doe"
|
||||||
|
push x0
|
||||||
|
|
||||||
|
ob.c x1, x1
|
||||||
|
msg x1, @selector[-init(name:age:)] ; x0 = new Person object
|
||||||
|
str x1, [bp, #1] ; p1 = new Person object
|
||||||
|
|
||||||
|
; p1 setAge:100 in:"months"
|
||||||
|
|
||||||
|
; p1 is already loaded in x1, no need to load it again
|
||||||
|
ldr x2, @selector[-setAge:in:]
|
||||||
|
|
||||||
|
ldr x3, "months"
|
||||||
|
push x3
|
||||||
|
|
||||||
|
ldr x3, #100
|
||||||
|
push x3
|
||||||
|
|
||||||
|
msg x1, @selector[-setAge:in:] ; x0 = null
|
||||||
|
|
||||||
|
ldr x3, #0
|
||||||
|
str x3, [bp, #2] ; i = 0
|
||||||
|
|
||||||
|
; (while i < 10) begin
|
||||||
|
L0001: ldr x2, @ident[cout] ; x5 = cout
|
||||||
|
ldr x4, "Count is "
|
||||||
|
|
||||||
|
add x4, x4, x3 ; append i (in x4) to "Count is " (in x7)
|
||||||
|
|
||||||
|
push x4
|
||||||
|
msg x2, @selector[-put:]
|
||||||
|
|
||||||
|
add x3, x3, #2
|
||||||
|
|
||||||
|
cmp x3, #100
|
||||||
|
b.lt $L0001
|
||||||
|
; (while i < 0) end
|
||||||
|
|
||||||
|
; for i in 0 to:100 step:2
|
||||||
|
ldr x4, #2
|
||||||
|
push x4
|
||||||
|
|
||||||
|
ldr x4, #100
|
||||||
|
push x4
|
||||||
|
|
||||||
|
ldr x4, #0
|
||||||
|
|
||||||
|
msg x5, @selector[-to:step:]
|
||||||
|
mov x5, x0 ; x5 = (0 to:100 step:2) -> iterator
|
||||||
|
|
||||||
|
; (for i in 0 to:100 step:2) begin
|
||||||
|
L0002: it.v x5 ; is iterator still valid?
|
||||||
|
b.z $L0003
|
||||||
|
|
||||||
|
it.g x4, x5 ; get current iterator value
|
||||||
|
|
||||||
|
ldr x6, "Count is "
|
||||||
|
add x6, x6, x4 ; append i (in x4) to "Count is " (in x6)
|
||||||
|
|
||||||
|
ldr x7, @ident[cout]
|
||||||
|
|
||||||
|
push x6
|
||||||
|
msg x7, @selector[-put:]
|
||||||
|
|
||||||
|
it.n x5 ; Advance iterator
|
||||||
|
br $L0002
|
||||||
|
; (for i in 0 to:100 step:2) end
|
||||||
|
|
||||||
|
L0003: ob.e x5 ; Clean up iterator
|
||||||
|
|
||||||
|
lam.c x5, @lambda[Lx0001] ; x5 = [ _:i | cout put:'Count: {i}' ],
|
||||||
|
|
||||||
|
push x5
|
||||||
|
|
||||||
|
ldr x5, #2
|
||||||
|
push x5
|
||||||
|
|
||||||
|
ldr x5, #100
|
||||||
|
push x5
|
||||||
|
|
||||||
|
ldr x5, #0
|
||||||
|
msg x5, @selector[-to:step:do:]
|
||||||
|
|
||||||
|
ldr x5, #32
|
||||||
|
str x5, [bp, #3] ; (x5) j = 32
|
||||||
|
|
||||||
|
ldr x6, [bp, #1] ; (x6) i
|
||||||
|
|
||||||
|
c.lt x5, x6, x5
|
||||||
|
|
||||||
|
lam.c x6, @lambda[Lx0003]
|
||||||
|
push x6
|
||||||
|
|
||||||
|
lam.c x6, @lambda[Lx0002]
|
||||||
|
push x6
|
||||||
|
|
||||||
|
msg x5, @selector[-if:else:]
|
||||||
|
|
||||||
|
ldr x5, [bp, #2] ; (x5) j
|
||||||
|
ldr x6, [bp, #1] ; (x6) i
|
||||||
|
|
||||||
|
cmp x6, x5
|
||||||
|
b.ge $L0004
|
||||||
|
|
||||||
|
ldr x5, "True!"
|
||||||
|
push x5
|
||||||
|
|
||||||
|
ldr x5, @ident[cout]
|
||||||
|
|
||||||
|
msg x5, @selector[-put:]
|
||||||
|
|
||||||
|
L0004: ret
|
||||||
|
@end
|
||||||
65
doc/sample/CaesarCipher.im
Executable file
65
doc/sample/CaesarCipher.im
Executable file
@@ -0,0 +1,65 @@
|
|||||||
|
package net.doorstuck.test
|
||||||
|
|
||||||
|
cout put:'Encode a string with a Caesar cipher.'
|
||||||
|
|
||||||
|
message = ''
|
||||||
|
|
||||||
|
while true do
|
||||||
|
cout print:'Message to encode: '
|
||||||
|
cout flush
|
||||||
|
|
||||||
|
cin get:message
|
||||||
|
|
||||||
|
if message != '' then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shiftWidth = 0
|
||||||
|
|
||||||
|
while true do
|
||||||
|
cout print:'Shift size: '
|
||||||
|
cout flush
|
||||||
|
|
||||||
|
shiftStr = ''
|
||||||
|
cin get:shiftStr
|
||||||
|
|
||||||
|
if shiftStr == '' then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
try
|
||||||
|
shiftWidth = Int parse:shiftStr
|
||||||
|
catch (#err:number_format, err)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
encodedMessage = ''
|
||||||
|
|
||||||
|
for c in message do
|
||||||
|
i = c toOrdinal
|
||||||
|
sub = 0
|
||||||
|
|
||||||
|
if i >= 65 && i <= 90 then
|
||||||
|
sub = 65
|
||||||
|
elif i >= 97 && i <= 122 then
|
||||||
|
sub = 97
|
||||||
|
else
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
i -= sub
|
||||||
|
i += shiftWidth
|
||||||
|
|
||||||
|
if i >= 26 then
|
||||||
|
i -= 26
|
||||||
|
end
|
||||||
|
|
||||||
|
c2 = i toChar
|
||||||
|
encodedMessage += c2
|
||||||
|
end
|
||||||
|
|
||||||
|
cout put:encodedMessage
|
||||||
20
doc/sample/FizzBuzz.im
Executable file
20
doc/sample/FizzBuzz.im
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
for i in 0 to:100 do
|
||||||
|
p = false
|
||||||
|
|
||||||
|
if i % 3 == 0 then
|
||||||
|
cout print:'fizz'
|
||||||
|
p = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if i % 5 == 0 then
|
||||||
|
cout print:'buzz'
|
||||||
|
p = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if !p then
|
||||||
|
cout print:'{i}'
|
||||||
|
end
|
||||||
|
|
||||||
|
cout cr
|
||||||
|
end
|
||||||
|
|
||||||
522
doc/sample/Person.im
Executable file
522
doc/sample/Person.im
Executable file
@@ -0,0 +1,522 @@
|
|||||||
|
/* example.im - An example Ivy Implementation source file */
|
||||||
|
|
||||||
|
package net.doorstuck.test
|
||||||
|
|
||||||
|
use std.io
|
||||||
|
|
||||||
|
-- Anything after a double hypen is ignored by the compiler
|
||||||
|
/*
|
||||||
|
Multi
|
||||||
|
Line
|
||||||
|
Comment
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
classes also double up as protocols.
|
||||||
|
|
||||||
|
any class can "pretend" to be another class by responding to the same
|
||||||
|
messages that the base class understands.
|
||||||
|
|
||||||
|
for example, by implementing all the messages that the Package class
|
||||||
|
understands, your class is said to implement the Package protocol,
|
||||||
|
allowing package-specific syntax (i.e. indexing and dot syntax)
|
||||||
|
to be used with your class.
|
||||||
|
|
||||||
|
there are two standard messages that complement this behaviour:
|
||||||
|
-is: takes a Class parameter, and returns true if the recipient
|
||||||
|
is an instance of the given Class.
|
||||||
|
-implements: takes a Class parameter, and returns true if the recipient
|
||||||
|
understands all of the messages that the parameter class
|
||||||
|
understands.
|
||||||
|
**/
|
||||||
|
class Person
|
||||||
|
/**
|
||||||
|
every message handler can have one or both of the following:
|
||||||
|
- a message name (in this case, 'init')
|
||||||
|
- a set of one or more parameters (wrapped in parentheses if the message
|
||||||
|
has a name)
|
||||||
|
|
||||||
|
each parameter has an internal name and an (optional) label, specified
|
||||||
|
as a pair of identifiers separated by a colon (:). the label comes
|
||||||
|
first, and is the name used by whoever is sending the message the
|
||||||
|
internal name comes second, and is used by the message handler itself as
|
||||||
|
a variable name.
|
||||||
|
|
||||||
|
you can omit the label for a parameter by specifying an underscore (_)
|
||||||
|
as the label, but this is not recommended. it looks weird, it obscures
|
||||||
|
the meaning of the message, and is really only supported for
|
||||||
|
compatibility with lambdas.
|
||||||
|
**/
|
||||||
|
- init(name:name age:age)
|
||||||
|
self.name = name
|
||||||
|
self.age = age
|
||||||
|
end
|
||||||
|
|
||||||
|
- test(param:data _:extra)
|
||||||
|
cout put:'Received {data}, {extra}'
|
||||||
|
end
|
||||||
|
|
||||||
|
- name
|
||||||
|
^self.name
|
||||||
|
end
|
||||||
|
|
||||||
|
- age
|
||||||
|
^self.age
|
||||||
|
end
|
||||||
|
|
||||||
|
- ageInMonths
|
||||||
|
^self.age * 12
|
||||||
|
end
|
||||||
|
|
||||||
|
- setName:name
|
||||||
|
self.name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
- setAge:age
|
||||||
|
self.age = age
|
||||||
|
end
|
||||||
|
|
||||||
|
- setAge:age inUnit:units
|
||||||
|
match units in
|
||||||
|
#years => self.age = age,
|
||||||
|
#months => self.age = age / 12,
|
||||||
|
#days => self.age = age / 365,
|
||||||
|
_ => self.age = 0,
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
- getAgeInUnit:units
|
||||||
|
^match units in
|
||||||
|
#years => age,
|
||||||
|
#months => age / 12,
|
||||||
|
#days => age / 365,
|
||||||
|
_ => 0,
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$ exampleProperty
|
||||||
|
- get
|
||||||
|
^42
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
p1 = Person new(name:'John Doe' age:34)
|
||||||
|
p1 setAge:100 inUnit:#months
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
when sending a message with unlabeled parameters, the preceding colon (:) is
|
||||||
|
still required. this is part of the reason why unlabeled parameters are
|
||||||
|
weird and not recommended.
|
||||||
|
**/
|
||||||
|
p1 test(param:'Hello' :'World')
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < 100 do
|
||||||
|
cout put:'Count is {i}'
|
||||||
|
i += 2
|
||||||
|
end
|
||||||
|
|
||||||
|
for i in 0 to:100 step:2 do
|
||||||
|
cout put:'Count is {i}'
|
||||||
|
end
|
||||||
|
|
||||||
|
/**
|
||||||
|
a lambda's parameters are *always* unlabeled, and the underscore (_) before
|
||||||
|
the label colon (:) is unnecessary.
|
||||||
|
**/
|
||||||
|
0 to:100 step:2 do:[ :i | cout put:'Count: {i}' ]
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
lambdas can capture state from the context in which they are created.
|
||||||
|
|
||||||
|
a lambda can reference any local variable that is accessible at the point
|
||||||
|
it is created. if a variable from such a context is referenced, its value
|
||||||
|
is captured at the point the lambda is constructed. the value of the
|
||||||
|
variable is copied into the lambda.
|
||||||
|
|
||||||
|
this means that, if a lambda references a variable in the outer context,
|
||||||
|
and the value of that variable changes between the lambda being created
|
||||||
|
and the lambda being executed, that change will not be reflected within
|
||||||
|
the lambda.
|
||||||
|
|
||||||
|
because the value of captured variables is copied into the lambda, it is
|
||||||
|
safe for a method to return a lambda that references variables in its
|
||||||
|
local context.
|
||||||
|
**/
|
||||||
|
q = 32
|
||||||
|
l = [ cout put:'Value of q is {q}' ] /* value of `q` is captured here */
|
||||||
|
q = 64
|
||||||
|
_ = l call /* prints 'Value of q is 32' */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
j = 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
expressions are terminated with a newline.
|
||||||
|
an expression can be written across multiple lines by using
|
||||||
|
the _ (underscore) line continuation character.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
keyword messages have a lower precedence than all non-assignment
|
||||||
|
binary operators, so (i < j) will be evaluated first, and
|
||||||
|
if:else: will be sent to the result.
|
||||||
|
**/
|
||||||
|
|
||||||
|
i < j
|
||||||
|
if:[ cout put:'True!' ]
|
||||||
|
else:[ cout put:'False!' ]
|
||||||
|
|
||||||
|
if i < j then
|
||||||
|
cout put:'True!'
|
||||||
|
end
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
package = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Packages can be indexed via integer or string. If a package is index with a
|
||||||
|
variable, the value of the variable is used as the index.
|
||||||
|
**/
|
||||||
|
|
||||||
|
package[0] = 16
|
||||||
|
|
||||||
|
/* All of these accesses are equivalent */
|
||||||
|
package['x'] = 32
|
||||||
|
package.x = 32
|
||||||
|
|
||||||
|
index = 'x'
|
||||||
|
package[index] = 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
this syntax, and the pkg.* instructions that it translates to, is
|
||||||
|
implemented behind-the-scenes by sending messages to the package.
|
||||||
|
|
||||||
|
if your custom object implements this protocol, package syntax can be used
|
||||||
|
on it too.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
these are the same packages used to store Classes and global variables.
|
||||||
|
for example, the std package is a regular package object, and you can use
|
||||||
|
package syntax on it or even send it messages.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/* integer constants can be written in all sorts of interesting formats */
|
||||||
|
i = 100
|
||||||
|
i = 0x100
|
||||||
|
i = 0200
|
||||||
|
i = 0b1010101
|
||||||
|
i = 1_000_000_000
|
||||||
|
|
||||||
|
/* tuples can be used to create a set of values */
|
||||||
|
|
||||||
|
tuple = (32, 'a string')
|
||||||
|
|
||||||
|
/**
|
||||||
|
they are similar to packages, except they only support consecutive integer
|
||||||
|
indices, and you cannot specify the indices when creating a tuple.
|
||||||
|
|
||||||
|
the main purpose of tuples is to allow unwrapping of iterator variables in
|
||||||
|
for loops.
|
||||||
|
**/
|
||||||
|
|
||||||
|
for (key, val) in package do
|
||||||
|
cout put:'{key} -> {val}'
|
||||||
|
end
|
||||||
|
|
||||||
|
/**
|
||||||
|
any object can be the target of a for-loop, as long as it meets one of the
|
||||||
|
following criteria:
|
||||||
|
a) is an Iterator object-- or
|
||||||
|
b) implements the Iterator protocol-- or
|
||||||
|
c) understands the -iterator message, and returns an object that itself
|
||||||
|
conforms to a) or b)
|
||||||
|
**/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
underscore (_) is used as a placeholder during assignment.
|
||||||
|
it can only appear on the left of an assignment, and anything
|
||||||
|
assigned to it is discarded.
|
||||||
|
*/
|
||||||
|
_ = 3 * 2
|
||||||
|
|
||||||
|
/* it is mostly used with pattern-matching and destructuring. */
|
||||||
|
|
||||||
|
a = (32, 64)
|
||||||
|
(_, v) = a
|
||||||
|
|
||||||
|
/* v is now equal to 64 */
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
try
|
||||||
|
v = Int parse:'342'
|
||||||
|
catch (#err:number_format, err) in
|
||||||
|
cout put:'Cannot parse integer string ({err msg})'
|
||||||
|
catch (_, err) in
|
||||||
|
cout put:'Unknown error occurred ({err msg})'
|
||||||
|
end
|
||||||
|
|
||||||
|
/* equivalent 'pure' syntax */
|
||||||
|
|
||||||
|
/**
|
||||||
|
this example also demonstrates how line continuations are not always
|
||||||
|
necessary when breaking a statement up over multiple lines.
|
||||||
|
|
||||||
|
the compiler uses a few conditions to determine if it should continue
|
||||||
|
parsing a particular statement past a line break:
|
||||||
|
|
||||||
|
1) if the next token immediately after a line break is a label.
|
||||||
|
2) if the token immediately before a line break if a binary operator.
|
||||||
|
3) if the line break occurs within a set of sub-expression delimiters
|
||||||
|
(parentheses), lambda delimiters (brackets), package delimiters
|
||||||
|
(braces), or string delimiters (single or double quotes).
|
||||||
|
|
||||||
|
note that if a line break is encountered in a string constant,
|
||||||
|
the line break character is included in the string. to prevent
|
||||||
|
this behaviour, you can precede the line break with a line continuation
|
||||||
|
character.
|
||||||
|
|
||||||
|
because of the implicit line continuations provided by these conditions,
|
||||||
|
the following statement requires no explicit line continuations to
|
||||||
|
be correctly parsed.
|
||||||
|
**/
|
||||||
|
|
||||||
|
[ v = Int parse:'342' ]
|
||||||
|
on:#err:number_format do:[ :err |
|
||||||
|
cout put:'Cannot parse integer string ({err msg})'
|
||||||
|
];
|
||||||
|
onError:[ :err |
|
||||||
|
cout put:'Unknown error occurred ({err msg})'
|
||||||
|
];
|
||||||
|
call
|
||||||
|
|
||||||
|
/**
|
||||||
|
this example doesn't meet any of the conditions required for implicit
|
||||||
|
line continuations to be inserted. because of this, it will be treated
|
||||||
|
as four separate statements.
|
||||||
|
|
||||||
|
there are a few ways this could be fixed:
|
||||||
|
|
||||||
|
1) surrount the right-hand side of the statement with parentheses.
|
||||||
|
2) put a line continuation character at the end of all but the last line.
|
||||||
|
**/
|
||||||
|
v = 5
|
||||||
|
squared
|
||||||
|
squared
|
||||||
|
squared
|
||||||
|
|
||||||
|
/**
|
||||||
|
below are some examples of throwing errors. when an error is thrown,
|
||||||
|
the runtimes unwinds the stack looking for the nearest error handler.
|
||||||
|
if no error handlers are found, the exception information is written
|
||||||
|
to cerr and exection stops.
|
||||||
|
|
||||||
|
the exact behaviour of a throw statement depends on what is thrown:
|
||||||
|
1) a string is thrown: a generic Error object is created.
|
||||||
|
Sending -msg to the Error returns the thrown string.
|
||||||
|
Sending -data to the Error returns null.
|
||||||
|
2) an object that implements the Error protocol is thrown: the
|
||||||
|
thrown object becomes the error object that is passed to the
|
||||||
|
error handler (or that is written to cerr when execution stops).
|
||||||
|
3) an object that doesn't implement the Error protocol is thrown:
|
||||||
|
a generic Error object is created.
|
||||||
|
sending -msg to the Error returns null.
|
||||||
|
sending -data to the Error returns the thrown object.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/* example 1) */
|
||||||
|
throw 'an error occurred')
|
||||||
|
|
||||||
|
/* example 2) */
|
||||||
|
throw { i => 32, s => 'error' }
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
x = 32
|
||||||
|
/* a whole bunch of ways of doing conditionals using lambdas and messages */
|
||||||
|
[ cout put:'Hello, world!' ] if:x > 10
|
||||||
|
[ cout put:'Hello, world!' ] unless:x <= 10
|
||||||
|
|
||||||
|
/* and the equivalent 'fancy' syntax */
|
||||||
|
|
||||||
|
cout put:'Hello, world!' if x > 10
|
||||||
|
cout put:'Hello, world!' unless x <= 10
|
||||||
|
|
||||||
|
/**
|
||||||
|
NOTE that keywords (if, end, try, and so on) can still be used as message
|
||||||
|
parameter names. however, they cannot be used as message names.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
Order of execution (precedence) for expression elements (listed from highest
|
||||||
|
to lowest precedence). elements that appear on the same list item have equal
|
||||||
|
precedence. in these cases, their associativity determines the order of
|
||||||
|
execution.
|
||||||
|
|
||||||
|
- unary operators
|
||||||
|
- boolean NOT (!)
|
||||||
|
- bitwise NOT (~)
|
||||||
|
- unary messages, complex messages
|
||||||
|
- binary operators
|
||||||
|
- multiplication (*)
|
||||||
|
- division (/)
|
||||||
|
- modulo (%)
|
||||||
|
- addition (+)
|
||||||
|
- subtraction (-)
|
||||||
|
- bitwise shift (<<, >>)
|
||||||
|
- (in)equality (==, !=)
|
||||||
|
- bitwise AND (&)
|
||||||
|
- bitwise XOR (^)
|
||||||
|
- bitwise OR (|)
|
||||||
|
- boolean AND (&&)
|
||||||
|
- boolean OR (||)
|
||||||
|
- cascade (;)
|
||||||
|
- inline if-else
|
||||||
|
- keyword messages
|
||||||
|
- binary operators
|
||||||
|
- assignment by product, quotient, and remainder (*=, /=, %=)
|
||||||
|
- assignment by sum and difference (+=, -=)
|
||||||
|
- assignment by bitwise shift (<<=, >>=)
|
||||||
|
- assignment by bitwise AND, XOR, and OR (&=, ^=, |=)
|
||||||
|
- assignment (=)
|
||||||
|
**/
|
||||||
|
|
||||||
|
p1
|
||||||
|
setAge:2 squared squared + 4 squared multiply(by:2 add:4 + 1 * 3)
|
||||||
|
in:'mon' + 'ths'
|
||||||
|
(p1
|
||||||
|
setAge:(((2 squared) squared) + ((4 squared) multiply(by:2 add:(4 + (1 * 3)))))
|
||||||
|
in:('mon' + 'ths'))
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/* how about package comprehension? */
|
||||||
|
|
||||||
|
/**
|
||||||
|
these two lines both construct a package containing all even numbers between
|
||||||
|
0 and 100 incremented by 1
|
||||||
|
|
||||||
|
the first one uses fancy list/package comprehension syntax.
|
||||||
|
the second one uses pure message/lambda syntax.
|
||||||
|
**/
|
||||||
|
data = { x + 1 for x in 0 to:100 if (x % 2) == 0 }
|
||||||
|
data = (0 to: 100) select:[ :x | ^(x % 2) == 0 ] collect:[ :x | ^x + 1 ]
|
||||||
|
|
||||||
|
/**
|
||||||
|
for a package comprehension that follows this template:
|
||||||
|
EXPRESSION for VARIABLE in COLLECTION if CONDITION
|
||||||
|
the equivalent -select:collect: message would look something like this:
|
||||||
|
COLLECTION select:[ VARIABLE | CONDITION ] collect:[ VARIABLE | EXPRESSION ]
|
||||||
|
|
||||||
|
-select:collect: is used to create a new package by filtering and
|
||||||
|
transforming an existing package.
|
||||||
|
|
||||||
|
To apply a filter without a subsequent transform, you can use -select:
|
||||||
|
To apply a transformation with a filter beforehand, you can use -map:
|
||||||
|
**/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
packages also support a -map: message for constructing a new package by
|
||||||
|
manipulating an existing one.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
this example creates a package of all ints between 1 and 5 using a regular
|
||||||
|
collection literal, and then uses -map: to create a second package by
|
||||||
|
doubling each of the numbers in the fist package.
|
||||||
|
**/
|
||||||
|
pkg1 = { 1, 2, 3, 4, 5 }
|
||||||
|
pkg2 = { x * 2 for x in pkg1 }
|
||||||
|
pkg2 = pkg1 map:[ :x | ^x * 2 ]
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
semicolon (;) is the cascade operator. it returns the recipient of the
|
||||||
|
previously sent message. it can be used to send multiple messages to a
|
||||||
|
single recipient.
|
||||||
|
**/
|
||||||
|
|
||||||
|
age = Person new(name:'John Doe' age:34);
|
||||||
|
setAge:144 inUnit:TimeUnit.MONTHS;
|
||||||
|
ageInMonths
|
||||||
|
|
||||||
|
-- age now has the value 144
|
||||||
|
-- the same behaviour can be achieved by using do..end
|
||||||
|
|
||||||
|
age = do
|
||||||
|
x = Person new(name:'John Doe' age:34)
|
||||||
|
x setAge:144 inUnit:TimeUnit.MONTHS
|
||||||
|
x ageInMonths
|
||||||
|
end
|
||||||
|
|
||||||
|
/**
|
||||||
|
this allows messages to be easily chained, without relying on the message
|
||||||
|
handler returning `self`.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
NOTE that cascade is a binary operator, and cannot be used without a
|
||||||
|
right-hand operand. otherwise, simply adding a semicolon to the end of an
|
||||||
|
expression would change the behaviour (and result) of the expression.
|
||||||
|
|
||||||
|
however, because cascade is a binary operator, it also supports implicit
|
||||||
|
line continuations.
|
||||||
|
|
||||||
|
for situations where you want to return the recipient after a chain of
|
||||||
|
cascaded messages, you can use the -yourself message, which is understood by
|
||||||
|
all objects by default and always returns the object itself.
|
||||||
|
**/
|
||||||
|
|
||||||
|
p1 = Person new(name:'John Doe' age:34);
|
||||||
|
setAge:100 inUnit:TimeUnit.MONTHS;
|
||||||
|
yourself
|
||||||
|
|
||||||
|
/* p1 now contains the Person object */
|
||||||
|
/* again, with do..end */
|
||||||
|
|
||||||
|
p1 = do
|
||||||
|
x = Person new(name:'John Doe' age:34)
|
||||||
|
x setAge:100 inUnit:TimeUnit.MONTHS
|
||||||
|
x
|
||||||
|
end
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
Blocks are a bit like lambdas, except they are part of the context of the
|
||||||
|
code that uses them. They are essentially a way of evaluating multiple
|
||||||
|
statements in a place where a single statement is usually expected.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/* Blocks are delimited by do..end, and the return operator can be used within
|
||||||
|
them. When it is, the value that is 'returned' will become the value that
|
||||||
|
the block as a whole evaluates to. also, the result of the last expression
|
||||||
|
evaluated in a block will be taken as its overall value. */
|
||||||
|
val = do
|
||||||
|
x = 3
|
||||||
|
y = 2
|
||||||
|
x * y
|
||||||
|
end
|
||||||
|
|
||||||
|
/* after this statement is executed, `val` will be equal to 6. */
|
||||||
|
|
||||||
6
doc/sample/String.im
Executable file
6
doc/sample/String.im
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
package net.doorstuck.test
|
||||||
|
|
||||||
|
s1 = 'hello world'
|
||||||
|
|
||||||
|
s2 = s1 map:[ :c | ^c uppercase ]
|
||||||
|
s3 = s1 map:[ :c | ^((c ordinal) + 1) toChar ]
|
||||||
29
doc/sample/Sum.im
Executable file
29
doc/sample/Sum.im
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
package net.doorstuck.test
|
||||||
|
|
||||||
|
cout put:'Finds the sum of a set of numbers.'
|
||||||
|
|
||||||
|
sum = 0
|
||||||
|
|
||||||
|
while true do
|
||||||
|
cout print:'Number (blank to finish): '
|
||||||
|
cout flush
|
||||||
|
|
||||||
|
input = ''
|
||||||
|
v = 0
|
||||||
|
cin get:input
|
||||||
|
|
||||||
|
if input == '' then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
try
|
||||||
|
v = Int parse:input
|
||||||
|
catch (#err:number_format, err)
|
||||||
|
cout put:'{input} is not a valid number.'
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
sum += v
|
||||||
|
end
|
||||||
|
|
||||||
|
cout put:'Sum: {sum}'
|
||||||
44
doc/sample/Vehicle.im
Executable file
44
doc/sample/Vehicle.im
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
package net.doorstuck.vehicles
|
||||||
|
|
||||||
|
protocol Vehicle
|
||||||
|
- startup
|
||||||
|
- shutdown
|
||||||
|
- nrWheels
|
||||||
|
- nrDoors
|
||||||
|
- turn(direction:dir)
|
||||||
|
- accelerate(direction:dir)
|
||||||
|
- velocity
|
||||||
|
end
|
||||||
|
|
||||||
|
class Car <Vehicle>
|
||||||
|
- startup
|
||||||
|
self.engineRunning = true
|
||||||
|
end
|
||||||
|
|
||||||
|
- shutdown
|
||||||
|
self.engineRunning = false
|
||||||
|
end
|
||||||
|
|
||||||
|
- nrWheels
|
||||||
|
^4
|
||||||
|
end
|
||||||
|
|
||||||
|
- nrDoors
|
||||||
|
^4
|
||||||
|
end
|
||||||
|
|
||||||
|
- turn(direction:dir)
|
||||||
|
self.currentDirection = dir
|
||||||
|
end
|
||||||
|
|
||||||
|
- accelerate(direction:dir)
|
||||||
|
match dir
|
||||||
|
#forward => self.currentVelocity += 1,
|
||||||
|
#backward => self.currentVelocity -= 1,
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
- velicity
|
||||||
|
^self.currentVelocity
|
||||||
|
end
|
||||||
|
end
|
||||||
91
doc/standard-library.txt
Executable file
91
doc/standard-library.txt
Executable file
@@ -0,0 +1,91 @@
|
|||||||
|
vim: tabstop=2 shiftwidth=2 expandtab
|
||||||
|
|
||||||
|
*** PACKAGES ******************************************************************
|
||||||
|
|
||||||
|
## std.lang #####
|
||||||
|
This package contains all of the "built-in" classes, such as String,
|
||||||
|
Package, etc.
|
||||||
|
|
||||||
|
This package is always included by default for all Ivy source files, so
|
||||||
|
there is no need to include a `use std.lang` statement.
|
||||||
|
|
||||||
|
|
||||||
|
## std.io #####
|
||||||
|
This package includes classes for performing IO operations, including
|
||||||
|
reading/writing to the terminals, files, etc.
|
||||||
|
|
||||||
|
This package also includes the cin, cout, and cerr objects that point
|
||||||
|
to standard in, standard out, and standard error respectively.
|
||||||
|
|
||||||
|
|
||||||
|
*** CLASSES *******************************************************************
|
||||||
|
|
||||||
|
//// std.lang ////
|
||||||
|
|
||||||
|
## Object #####
|
||||||
|
-is:
|
||||||
|
-implements:
|
||||||
|
-respondsTo:
|
||||||
|
-doesNotUnderstand(selector:args:)
|
||||||
|
-sendMsg(selector:args:)
|
||||||
|
-toString
|
||||||
|
|
||||||
|
## Int #####
|
||||||
|
+parse:
|
||||||
|
-to:
|
||||||
|
-to:step:
|
||||||
|
-to:step:do:
|
||||||
|
|
||||||
|
## Enum #####
|
||||||
|
**
|
||||||
|
|
||||||
|
## String #####
|
||||||
|
-size
|
||||||
|
-uppercase
|
||||||
|
-lowercase
|
||||||
|
-append:
|
||||||
|
-prepend:
|
||||||
|
-at:insert:
|
||||||
|
-contains:
|
||||||
|
-iterator
|
||||||
|
|
||||||
|
## Bool #####
|
||||||
|
-if:
|
||||||
|
-if:else:
|
||||||
|
|
||||||
|
## Package #####
|
||||||
|
-size
|
||||||
|
-exists:
|
||||||
|
-at:put:
|
||||||
|
-at:
|
||||||
|
-map:
|
||||||
|
-select:
|
||||||
|
-select:collect:
|
||||||
|
-iterator
|
||||||
|
|
||||||
|
## Iterator #####
|
||||||
|
-value
|
||||||
|
-moveNext
|
||||||
|
-select:
|
||||||
|
-select:collect:
|
||||||
|
-iterator
|
||||||
|
|
||||||
|
## Lambda #####
|
||||||
|
-argCount
|
||||||
|
-call
|
||||||
|
-call:
|
||||||
|
-call(_:_:)
|
||||||
|
-call(_:_:_:)
|
||||||
|
-call(_:_:_:_:)
|
||||||
|
-call(args:)
|
||||||
|
-on:do:
|
||||||
|
-if:
|
||||||
|
-unless:
|
||||||
|
|
||||||
|
## Selector #####
|
||||||
|
+parse:
|
||||||
|
|
||||||
|
## Error #####
|
||||||
|
-msg
|
||||||
|
-data
|
||||||
|
-withMsg:andData:
|
||||||
48
doc/vm-instructions.txt
Executable file
48
doc/vm-instructions.txt
Executable file
@@ -0,0 +1,48 @@
|
|||||||
|
ldr <dest:REG> <src:SP-offset>
|
||||||
|
ldr <dest:REG> <src:BP-offset>
|
||||||
|
ldr <dest:REG> <src:SELF-offset>
|
||||||
|
ldr <dest:REG> <src:SELF-offset>
|
||||||
|
ldr <dest:REG> <src:IMM>
|
||||||
|
ldr <dest:REG> <src:POOL_INDEX>
|
||||||
|
|
||||||
|
str <src:REG> <dest:SP-offset>
|
||||||
|
str <src:REG> <dest:BP-offset>
|
||||||
|
str <src:REG> <dest:SELF-offset>
|
||||||
|
|
||||||
|
push <value:REG>
|
||||||
|
pop <dest:REG>
|
||||||
|
|
||||||
|
add <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
sub <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
mul <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
div <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
|
||||||
|
cmp <value1:REG> <value2:REG>
|
||||||
|
|
||||||
|
c.eq <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
c.ne <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
c.lt <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
c.le <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
c.gt <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
c.ge <dest:REG> <value1:REG> <value2:REG>
|
||||||
|
|
||||||
|
br <dest:IMM>
|
||||||
|
b.z <dest:IMM>
|
||||||
|
b.nz <dest:IMM>
|
||||||
|
b.eq <dest:IMM>
|
||||||
|
b.ne <dest:IMM>
|
||||||
|
b.lt <dest:IMM>
|
||||||
|
b.le <dest:IMM>
|
||||||
|
b.gt <dest:IMM>
|
||||||
|
b.ge <dest:IMM>
|
||||||
|
|
||||||
|
ob.c <dest:REG> <class:POOL_INDEX>
|
||||||
|
ob.e <ref:REG>
|
||||||
|
|
||||||
|
lam.c <dest:REG> <body:POOL_INDEX>
|
||||||
|
|
||||||
|
it.g <dest:REG> <iterator:REG>
|
||||||
|
it.n <iterator:REG>
|
||||||
|
it.v <iterator:REG>
|
||||||
|
|
||||||
|
ret
|
||||||
Reference in New Issue
Block a user