Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: a83b4bab8b
Fetching contributors…

Cannot retrieve contributors at this time

3308 lines (2066 sloc) 92.268 kb
Red/System Language Specification
Author: Nenad Rakocevic
Date: 19/08/2012
Revision: 35
Status: reference document
Home: <a href="http://www.red-lang.org">red-lang.org</a>
===Abstract
Red/System is a dialect (<a href="http://en.wikipedia.org/wiki/Domain-specific_language" target="_new">DSL</a>) of the Red programming language. Its purpose is to provide:
*low-level system programming capabilities
*a tool to build Red's runtime low-level library
*a tool to link code and produce executables
Red/System can be seen as a C-level language with memory pointer support and a very basic and limited set of datatypes.
<u>Implementation note</u>: It is currently provided with a complete tool-chain generating executables from source files. This is a temporary state as Red/System will live inside Red, so will be embedded in Red scripts.
===Syntax
The syntax is almost the same as the one used by REBOL language, as the lexer (<a href="http://www.rebol.com/docs/words/wload.html" target="_new">LOAD</a>) is currently provided by REBOL during the bootstrapping phase. The REBOL syntax does not have a formal specification nor an exhaustive documentation, just a superficial description, but it is enough to work with. See:
*<a href="http://www.rebol.com/docs/core23/rebolcore-3.html" target="_new">http://www.rebol.com/docs/core23/rebolcore-3.html</a> (There is a typo in the table, all literals are missing a caret (^) character after the first quote)
*<a href="http://www.rebol.com/r3/docs/guide/code-syntax.html" target="_new">http://www.rebol.com/r3/docs/guide/code-syntax.html</a>
A complete syntax specification for both Red and Red/System will be provided during the implementation of the Red language layer.
For now, Red/System uses 8-bit character encoding (ASCII). Once proper Unicode support will be provided by the Red language layer, Red/System will switch to UTF-8 source encoding.
Here are a few practical aspects of the language syntax:
---Delimiters
String delimiters: double quotes
"this is a string"
{This is
a multiline
string.
}
Block of code delimiters: square brackets
if a > 0 [print "TRUE"]
either a > 0 [print "TRUE"][print "FALSE"]
while [a > 0][print "loop" a: a - 1]
Path separator: slash (denotes a hierarchical relation)
s: struct [i [integer!] b [byte!]]
s/i: 123
s/b: #"A"
---Free-form syntax
Red/System (and Red) inherits the free-form syntax of the REBOL language. The only syntactic constraints are putting a whitespace (in the large sense) between tokens and correctly pairing delimiters.
Examples of valid code:
while [a > 0][print "loop" a: a - 1]
while [a > 0]
[print "loop" a: a - 1]
while [
a > 0
][
print "loop"
a: a - 1
]
Code guidelines are not yet available. They will follow standard REBOL practices.
---Comments
Inline comment:
;this is a commented line
print "hello world" ; this is another comment
Multiline comment:
comment {
This is a
multiline
comment
}
Usage rules:
*Inline comments are allowed anywhere in the source code
*Multi-line comments are allowed anywhere in the source code, except in expressions. Exemple:
a: 1 + comment {5} 4 ; this will produce a compilation error
===Variables
Variables are labels used to represent a memory location. The labels (called <b>identifiers</b> from now) are formed by sequences of printable characters without any blank (space, newlines or tabulation). Printable characters are defined as any one-byte character in the 20h-7Eh range that can be printed out in system's console excepting the following ones (used as delimiters or reserved for some datatypes literals):
[ ] { } " ( ) / \ @ # $ % ^ , : ; < >
There is a restriction on the first character, the following characters are forbidden in the first position, but allowed at other positions:
0 1 2 3 4 5 6 7 8 9 '
Also there is a another restriction to avoid letting the compiler mistake an hex integer for a variable name. Variable names starting with A-F letters consisting of 2, 4 or 8 A-F and 0-9 characters and ending with h are not allowed.
All identifiers (variables and function names) are <u>case-insensitive</u>.
\note Unicode support
As indicated in the Syntax section, Unicode support is not available during the bootstrapping phase, it will be available at Red layer, so Red/System will inherit it once rewritten in Red.
/note
---Setting a value
Variables can hold any value of the available datatypes. This can be the real value (like integer! or pointer!) or a reference to the real value as is the case for struct! or c-string!). To assign a value to a variable, use a colon character at the end of the variable identifier.
foo: 123
bar: "hello"
\note Multiple assignments
Multiple assignments, like a: b: 123, are not currently supported in Red/System. Such feature will be added in the future at some point, probably on the rewrite of Red/System in Red.
/note
---Getting a value
Just use the variable name without any decoration to get its value or to pass it as a function's argument.
bar: "hello"
print bar
will output:
hello
---Variable's type
Variables do have a type. Variables do not need to be declared before being used, but they require to be <u>initialized</u> anyway. Function local variables require to be declared, but the type specification part can be skipped if the variable is properly initialized. For example:
foo: 123
bar: "hello"
size: length? bar
id: GetProcessID ;-- 'GetProcessID would return an integer!
compute: func [
a [integer!]
return: [integer!]
/local c ;-- 'c is declared without a type
][
c: 1 ;-- inferred type is integer!
a + c
]
are valid variable usages.
Initializations have to be done at root level of code, attempt to initialize from a block of code will result in a compilation error.
foo: 123 ;-- valid initialization
if a < b [foo: 123] ;-- invalid initialization
<u>Note</u>: A function body block is considered a root level.
Allowed types for variables are:
*integer!
*byte!
*float!
*float32!
*logic!
*c-string!
*struct!
*pointer!
===Datatypes
---Integer!
+++Literal format
decimal form : 1234
decimal negative form : -1234
hexadecimal form : 04D2h
The integer! datatype represents natural and negative natural numbers. The memory size of an integer is 32 bits, so the range of supported numbers is :
-2147483648 to 2147483647
<b>Hexadecimal format</b>
Hexadecimal integer representation is mostly used to represent memory addresses or binary data for bitwise operations. As for character, all hexadecimal literals found in sources are converted to their integer decimal value during lexical analysis. Allowed range is:
00000000h to FFFFFFFFh
Hex letters have to be written in <b>uppercase</b> and only 2, 4 and 8 characters are allowed (prefixing with leading zeros is allowed).
\note Hex literal form design decision
The 0x prefix is often used to mark a literal hexadecimal value. It could have been used in Red/System too if the &lt;number&gt;x&lt;number&gt; literal form wasn't reserved in Red for the pair! datatype. As Red/System is a dialect of Red, it has to use the same representation for hex values, so <b>&lt;hexa&gt;h</b> was chosen instead.
/note
---Byte!
The byte! datatype's purpose is to represent unsigned integers in the 0-255 range. byte! is an alias for the internal uint8! datatype, both will be accepted, but byte! is the official one.
+++Syntax
#"<character>"
#"^<character>"
#"^(hexadecimal)"
#"^(name)"
Examples:
#"a"
#"A"
#"5"
#"^A"
#"^(1A)"
#"^(back)"
See <a href="http://www.rebol.com/docs/core23/rebolcore-16.html#section-3.1" target="_new">http://www.rebol.com/docs/core23/rebolcore-16.html#section-3.1</a> for a more complete description of this format.
+++Casting
Casting is allowed to some extent (see section "4.9 Type Casting").
foo: as integer! #"a" ;-- foo holds 97
bar: as byte! foo ;-- bar holds #"a"
<u>Note</u>: trying to cast an integer value greater than 255 to a byte! will result in a data loss or data corruption. <i>(The handling of this case might be changed in future revisions)</i>
---Float!
The float! datatype represents an IEEE-754 double precision floating point number. Float! memory size is 64-bit.
<u>Note</u>: <b>float64!</b> can be used as an alias to <b>float!</b>.
+++Syntax
<sign><digits>.<digits>
or using scientific notation:
<sign><digits>E<exponent>
<sign><digits>.<digits>E<exponent>
where:
<sign> : an optional + or - symbol
<digits> : one or more digits
<exponent> : a positive or negative integer
Examples:
0.0
1.0
-12345.6789
3.14159265358979
-1E3
+1.23456E-265
A maximum of 16 digits are accepted for literal float! values, if more are specified, they will be dropped.
<i>For more information on double precision floating point numbers, see <a href="http://en.wikipedia.org/wiki/Double-precision_floating-point_format">Wikipedia</a></i>.
+++Casting
It is allowed to apply a type casting transformation on a float! value to convert it to a float32! value.
Examples:
pi: 3.14159265358979
pi-32: as float32! pi
print pi-32
will output
3.1415927
+++Math operations
All Red/System math operators (+, -, *, /, //, %) are supported. The default rounding method on results is "rounding to nearest". Both operands need to be of float! types (no implicit casting).
The modulo and remainder operators have a special meaning when used on float! values:
* modulo (//): floating point remainder (result is a float!).
* remainder (%): IEEE-754 rounded remainder (result is an integer!).
---Float32!
The float32! datatype represents an IEEE-754 single precision floating point number. Float! memory size is 32-bit.
\note Float32! purpose
The reason for having a single precision floating point type is for making the interfacing with popular libraries straightforward. For pure Red/System programs, <b>float!</b> should be the default choice.
/note
+++Syntax
There is no literal form for float32! datatype values. To load a float32! constant, the method consist in provindg a float! literal and prefix it with a type casting to float32!.
Example:
pi32: as float32! 3.1415927
<i>For more information on single precision floating point numbers, see <a href="http://en.wikipedia.org/wiki/Single-precision_floating-point_format">Wikipedia</a></i>.
+++Casting
It is allowed to apply a type casting transformation on a float32! value to convert it to a float! value. Type casting to integer! is also allowed mainly for bits manipulation purpose (this is not a float32! number to integer! number conversion).
Examples:
s: as float32! 3.1415927
print [
as float! s lf
as integer! s
]
will output
3.14159270000000
1518260631
+++Math operations
All Red/System math operators (+, -, *, /, //, %) are supported. The default rounding method on results is "rounding to nearest". Both operands need to be of float32! types (no implicit casting).
The modulo and remainder operators have a special meaning when used on float32! values:
* modulo (//): floating point remainder (result is a float32!).
* remainder (%): IEEE-754 rounded remainder (result is an integer!).
---Logic!
The logic! datatype represents boolean values: <b>TRUE</b> and <b>FALSE</b>. Logic variables are initialized using a literal logic value or the result of a conditional expression.
As a first class datatype, you can pass logic values and variables as function arguments or use them as function's return value.
+++Literal format
true
false
Using a literal to initialize a logic variable:
foo: true
either foo [print "true"][print "false"]
will output:
true
Using a conditional expression to initialize a logic variable:
bar: 2 > 5
either bar [print "true"][print "false"]
will output:
false
---C-string!
A c-string! value is a sequence of non-null bytes terminated by a null byte. A c-string variable holds the memory address of the first byte of the c-string, so it can be viewed as an implicit pointer to a variable of the byte! datatype. A c-string having a null character as first byte is an empty c-string.
+++Syntax
Literal c-strings are defined using double quotes delimiters or a pair of matching curly braces:
foo: "I am a c-string"
bar: {I am
a multiline
c-string
}
\note C-string null byte ending
You don't have to add a null byte to literal c-strings, it is added automatically during compilation.
/note
+++C-string length
It is possible to retrieve the number of bytes (<b>excluding the null</b> end marker) in a c-string at runtime using the LENGTH? function:
a: length? "Hello" ;-- here length? will return 5
\note Length? vs Size?
Do not confuse the <b>length?</b> function with the <b>size?</b> function. <b>Size?</b> function will return the number of bytes in the c-string, including the ending null byte.
/note
+++C-string arithmetic
It is possible to apply some simple math operations on c-string variables like additions and subtractions. C-string address would be increased or decreased by the integer argument.
Syntax
<c-string> + <n>
<c-string> - <n>
<c-string> : c-string variable
<n> : expression resulting in an integer! value
Example
s: "hello" ;-- let's suppose s points to address 4000000h
s: s + 1 ;-- now s points to address 40000001h
print s ;-- "ello" would be printed
s: s + 1 ;-- now s points to address 40000002h
print s ;-- "llo" would be printed
+++Accessing bytes
It is possible to access individual bytes in a c-string using path notation:
<c-string>/integer! ;-- literal integer index provided
<c-string>/<index> ;-- index provided by a variable
<c-string> : a c-string variable
<index> : an integer variable
The returned value will be of the type <b>byte!</b>.
Examples:
foo: "I am a c-string"
foo/1 => #"I" ;-- byte! value (73)
foo/2 => #" " ;-- byte! value (32)
...
foo/15 => #"g" ;-- byte! value (103)
foo/16 => #"^(00)" ;-- byte! value (0) (end marker)
\note Important notes
*In contrary to the C language, indexes in Red/System are <b>one-based</b>.
*The behaviour of path with an index out of bounds is not yet defined. (It is better to avoid it)
/note
Example of a variable used as index:
c: 4
foo/c => #"m" ;-- byte! value (109)
A simple way to traverse a c-string would be:
foo: "I am a c-string"
bar: foo
until [
print bar/1
bar: bar + 1
bar/1 = null-byte
]
will output:
I am a c-string
Similarly, it is also possible to modify the c-string's bytes using path notation with an ending colon:
<c-string>/integer!: <value> ;-- literal integer index provided
<c-string>/<index>: <value> ;-- index provided by a variable
<c-string> : a c-string variable
<index> : an integer variable
<value> : a byte! value
For example:
foo: "I am a c-string"
foo/3: #"-"
c: 4
foo/c: #"-"
print foo
will output
I -- a c-string
---Struct!
Struct! datatype is roughly equivalent to C struct type. It is a record of one or several values, each value having its own datatype. A struct variable holds the memory address of a struct value.
<u>Implementation note</u>: Struct! values members are <u>padded</u> in memory in order to preserve optimal alignment for each target (for example, it is aligned to 4 bytes for IA32 target). <b>Size?</b> function will return the size of the struct! value in memory including the padding bytes.
+++Declaration
Declaring a struct! value is achieved by using the DECLARE STRUCT! sequence followed by a specification block. That block defines struct! value members using pairs of name and datatype definition.
declare struct! [
<member> [<datatype>]
...
]
<member> : a valid identifier
<datatype> : integer! | byte! | pointer! [integer! | byte!] | logic! |
float! | float32! | c-string! | struct! [<members>]
The returned value of DECLARE STRUCT! is the memory address of the newly created struct! value.
<u>Note</u>: Struct members are all initialized to 0 when a new literal struct! is declared.
+++Usage
s: declare struct! [
a [integer!]
b [c-string!]
c [struct! [d [integer!]]]
]
In this example, the struct value has 3 members: a, b, c, each with a different datatype. The c member is a struct! value pointer, it needs to be assigned to a struct value to be used. So a correct initialization for the c member would be:
s/c: declare struct! [d [integer!]]
+++Accessing members
Member access is achieved using path notation. Syntax is:
<struct>/<member> ;-- read access
<struct>/<member>: <value> ;-- write access
<struct> : a valid struct variable
<member> : a valid member identifier in <struct>
<value> : a value of same datatype as <member>
From last example, that would give:
foo: s/a ;-- reading member 'a in struct 's
s/a: 123 ;-- writing 123 in member 'a in struct 's
s/b: "hello"
bar: s/c/d ;-- deep read/write access is also possible
+++Struct arithmetic
It is possible to apply some simple math operations on struct variables like additions and subtractions. Struct address would be increased or decreased by the size of the pointed struct value multiplied by the integer argument.
Syntax
<struct> + <n>
<struct> - <n>
<struct> : struct variable
<n> : integer value
Examples
p: declare struct! [ ;-- let suppose p = 4000000h
a [integer!]
b [pointer! [integer!]]
] ;-- struct memory size would be 8 bytes
p: p + 1 ;-- now p = 40000008h
<u>Note</u>: The struct value size is target and alignment dependent. In the above example, it is supposed to run on a 32-bit system with a struct alignment to 4 bytes.
+++Aliases
Struct! values definitions tend to be quite long, so in some cases, when struct! definitions are required to be inserted in other struct! definitions or in functions specification block, it is possible to use an alias name to reference a struct! definition through the source code. It also allows the self-referencing case to be quite simply solved.
<u>Notes</u>:
* An alias is not a value, it doesn't take any space in memory, it can be seen as a <i>virtual datatype</i>. So, by convention, alias names should end with an exclamation mark, in order to distinguish them more easily from variables in the source code.
* Aliased names live in their own namespace, so they cannot interfere with variable names.
Aliasing syntax:
<name>: alias struct! [
<member> [<datatype>]
...
]
<name> : the name to use as alias
<member> : a valid identifier
<datatype> : integer! | byte! | pointer! [integer! | byte!] | logic! |
float! | float32! | c-string! | struct! [<members>]
Struct value declaration using an aliased definition:
<variable>: declare <alias>
<variable> : a struct variable
<alias> : a previously declared alias name
Struct usage example:
book!: alias struct! [ ;-- defines a new aliased type
title [c-string!]
author [c-string!]
year [integer!]
last-book [book!] ;-- self-referenced definition
]
gift: declare struct! [
first [book!] ;-- reference to a book! struct
second [book!] ;-- reference to a book! struct
]
gift/first: declare book! ;-- book! struct allocation
gift/first/title: "Contact"
gift/first/author: "Carl Sagan"
gift/first/year: 1985
---Pointer!
Pointer datatype purpose is to hold the memory address of another value. A pointer value is defined by the pointed value address and datatype. As c-string! and struct! are already implicit pointers, the only pointed datatypes allowed are integer! and byte! (logic! pointer is not needed).
Byte! pointers are equivalent to c-string! references, the difference lies only in the interpretation of the pointed values. Byte! pointer is meant to point to a stream of byte without a specified bound, while c-string! references an array of bytes terminated by a null byte.
<u>Implementation note</u>: The memory size of a pointer is 4 bytes on 32-bit systems (and 8 bytes on 64-bit systems).
+++Literal format
New pointers value can be created using the following syntax:
declare pointer! [<datatype>]
<datatype>: integer! | byte! | float! | float32!
\note Possible syntactic sugar
The & symbol used in previous revisions of this document has been removed due to the new limited pointer! datatype usage. It could be reintroduced again in the future if required.
/note
Examples:
foo: declare pointer! [integer!] ;-- equivalent to C's: int *foo;
bar: declare pointer! [byte!] ;-- equivalent to C's: char *bar;
baz: declare pointer! [float!] ;-- equivalent to C's: double *baz;
\note Pointer value initialization
Do not assume any default value for a pointer value until it is initialized properly. In the current implementation, global pointer variables are set to 0 by default while local pointer variables default value is undefined. This might change in the future to adopt a default value more suitable for debugging (like 0BADBAD0h or similar hex trick).
/note
+++Declaration
Pointer declaration is only required for local pointer variables in functions' specification block. In such case, the datatype declaration can be omitted and left to the inferencer to guess. (See "Type inference" section)
pointer! [<datatype>]
<datatype>: integer! | byte! | float! | float32!
Global variables declaration examples (with C equivalents):
p: declare pointer! [integer!] ;-- int *p;
p: declare pointer! [byte!] ;-- char *p;
p: declare pointer! [float!] ;-- double *p;
Same with local variables declaration examples (with C equivalents):
func [/local p [pointer! [integer!]] ;-- int *p;
func [/local p [pointer! [byte!]] ;-- char *p;
func [/local p [pointer! [float!]] ;-- double *p;
Example of inferred pointer variable type:
foo: func [
a [struct! [count [integer!]]]
/local
p [pointer! [integer!]] ;-- explicit declaration
][
foobar p ;-- foobar modifies p
a/count: p/value
]
bar: func [
a [struct! [count [integer!]]]
/local p ;-- p datatype inferred
][
p: declare pointer! [integer!] ;-- p initialized (implicit declaration)
foobar p
a/count: p/value
]
bar2: func [
a [struct! [count [integer!]]]
/local p ;-- p datatype inferred
][
p: GetPointer a ;-- datatype is guessed from return value
foobar p
a/count: p/value
]
+++Dereferencing
Dereferencing a pointer is the operation allowing to access the pointed value. In Red/System, it is achieved by adding a <b>/value</b> refinement to the pointer variable (called more generally "path notation"):
<pointer>/value ;-- read access
<pointer>/value: <value> ;-- write access
<pointer> : pointer variable
<value> : a value of same type as in pointer's definition
Usage example
p: declare pointer! [integer!] ;-- declare a pointer on an integer
bar: declare pointer! [integer!] ;-- declare another pointer on an integer
p: as [pointer! [integer!]] 40000000h ;-- type cast an integer! to a pointer!
p/value: 1234 ;-- write 1234 at address 40000000h
foo: p/value ;-- read pointed value back
bar: p ;-- assign pointer address to 'bar
<u>Note</u>: Remember that a pointed value is undefined (can contain an arbitrary value) until you define it explicitly
+++Pointer arithmetic
It is possible to apply some simple math operations on pointers like additions and subtractions (as in C). A pointer address will be increased or decreased by the memory size of the pointed value multiplied by the amount to respectively add or subtract.
p: declare pointer! [integer!] ;-- pointed value memory size is 4 bytes
p: as [pointer! [integer!]] 4000000h
p: p + 1 ;-- p points now to 40000004h
p: p + 1 ;-- p points now to 40000008h
q: declare pointer! [byte!] ;-- pointed value memory size is 1 byte
q: as [pointer! [byte!]] 4000000h
q: q + 1 ;-- p points now to 40000001h
q: q + 1 ;-- p points now to 40000002h
Also, additions and subtractions between pointer addresses are allowed. The result value type is, as usual, the type of left operand.
offset: p - q ;-- would store 6 in offset
;-- type of offset is pointer! [integer!]
+++Pointer path notation
It is possible to use path notation to simulate an array with indexed access. Both reading and writing are possible. Indexes are <b>one-based</b>.
<b>Syntax</b>
<pointer>/<integer> ;-- literal integer index provided
<pointer>/<index> ;-- index provided by a variable
<pointer> : a pointer variable
<integer> : an integer literal value
<index> : an integer variable
Examples:
p: declare pointer! [integer!]
p: as [pointer! [integer!]] 4000000h
a: p/1 ;-- reads an integer! from 40000000h
p/2: a ;-- writes the integer! to 40000004h
Integer variable can also be used as index:
p: declare pointer! [integer!]
p: as [pointer! [integer!]] 4000000h
c: 2
p/c: 1234 ;-- writes 1234 (4 bytes) at 40000004h
<u>Note</u>: Pointer's <b>/value</b> notation is strictly equivalent to <b>/1</b>. The <b>/value</b> notation can be considered as syntactic sugar.
+++Null value
A special <b>null</b> value is available to use for pointer! and other pointer-like (pass-by-reference) types (struct!, c-string!) and pseudo-type function!. <b>Null</b> does not have a specific type, but can be used to replace any other pointer-like value. So, <b>null</b> cannot be used as initializing value for a global variable or a local variable that does not have an explicit type specification.
<b>Null</b> is a first class value, so it can be assigned to a variable, passed as argument to a function or returned by a function.
<u>Note</u>: It is not possible to explicitly cast <b>null</b> to a given type, only implicit type casting automatically done by the compiler is allowed.
Examples:
a: declare pointer! [integer!]
a: null ;-- valid assignment, 'a type is defined
b: null ;-- invalid assignment, type of b cannot
;-- be deduced by the compiler
foo: func [s [c-string!] return: [c-string!]][
if s = null [
print "error"
return null
]
return uppercase s
]
b: foo "test" ;-- will set b to "TEST"
b: foo null ;-- will print "error" and set b to null
+++C void pointer
There is no specific support in Red/System for C-like void pointers. The official way is to use a pointer! [byte!] type to represent C void* pointers.
For pointers to c-string! or struct! variables, a pointer variable can be used then dereferenced and type cast to the target type.
Example:
p-buffer!: alias struct! [buffer [c-string!]]
get-hello: function [
s [p-buffer]
return: [p-buffer] ;-- returns a pointer to a c-string
][
s/buffer: "hello"
s ;-- equivalent to C's char **
]
foo: func [
/local
c [pointer! [integer!]]
s [c-string!]
][
c: get-hello
s: as c-string! c/value
print s
]
would print
hello
\note Runtime macro byte-ptr!
The runtime defines a byte-ptr! macro (just defined as: pointer! [byte!]) to be used as an equivalent to C void* for raw memory accesses.
/note
+++Variable pointer
It is possible to get a pointer on an existing variable for the following datatypes:
* integer!
* byte!
* float!
* float32!
<b>Syntax</b>
:<variable>
<variable> : a variable name of allowed type.
This expression will return a pointer value which type depends on the variable type, so:
<div align="center">
<table class="normal">
<tr>
<th>variable</th>
<th>:variable</th>
</tr><tr>
<td>integer!</td>
<td>pointer! [integer!]</td>
</tr><tr>
<td>byte!</td>
<td>pointer! [byte!]</td>
</tr><tr>
<td>float!</td>
<td>pointer! [float!]</td>
</tr><tr>
<td>float32!</td>
<td>pointer! [float32!]</td>
</tr>
</table>
</div>
Example:
s: declare pointer! [integer!]
a: 123
s: :a
print s/value ;-- will output 123
\note Pointer on local variable
It is allowed to get a pointer on a local variable, however, <u>special attention</u> should be provided to avoid using such pointer once the function has returned, this would result in most cases in crashes caused by stack corruption!
/note
---Type Casting
Casting is achieved using the <b>AS</b> keyword followed by the target type and the value to cast.
Type casting is possible between value of compatible types. Compatible types are defined in the following type casting reference matrix. A run-time type conversion might be generated for some types combinations.
<u>Note</u>: Trying to assign a value to a variable of different type without a proper type casting, will result in a compilation error.
<b>Syntax</b>
as <new-type> value
as [<new-type>] value ;-- alternative syntax
<new-type> : integer! | byte! | logic! | c-string! | float! | float32! |
pointer! [integer!] | struct! [<members>] |
<alias-name>
<u>Note</u>: Multiple nested type castings are not allowed and will raise a compilation error.
Example:
foo: 0 ;-- foo is an integer variable
bar: declare pointer! [integer!] ;-- bar is a pointer variable
foo: as integer! bar ;-- type casting
bar: as pointer! [integer!] foo
<br>
<b>Type casting reference matrix</b>
Keep in mind that pointer!, c-string!, struct! and function! are passed by reference, so the casting below for these datatypes is applied on their memory address value.
<div align="center">
<table class="matrix">
<tr>
<th id="matrix-start">source&gt;&gt;</th>
<th>byte!</th>
<th>integer!</th>
<th>logic!</th>
<th>c-string!</th>
<th>pointer!</th>
<th>struct!</th>
<th>float!</th>
<th>float32!</th>
<th>function! &sup2;</th>
</tr><tr>
<th>byte!</th>
<td class="warn">WARNING</td>
<td class="allow">to byte! &sup1;</td>
<td class="allow">true<span class="arrow">&raquo;</span>#"^(01)"<br>false<span class="arrow">&raquo;</span>#"^(00)"</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
</tr><tr>
<th>integer!</th>
<td class="allow">to integer!</td>
<td class="warn">WARNING</td>
<td class="allow">true<span class="arrow">&raquo;</span>1<br>false<span class="arrow">&raquo;</span>0</td>
<td class="allow">to integer!</td>
<td class="allow">to integer!</td>
<td class="allow">to integer!</td>
<td class="deny">ERROR</td>
<td class="allow">as integer!</td>
<td class="allow">as integer!</td>
</tr><tr>
<th>logic!</th>
<td class="allow">#"^(00)"<span class="arrow">&raquo;</span>false<br>else<span class="arrow">&raquo;</span>true</td>
<td class="allow">0<span class="arrow">&raquo;</span>false<br>else<span class="arrow">&raquo;</span>true</td>
<td class="warn">WARNING</td>
<td class="allow">null<span class="arrow">&raquo;</span>false<br>else<span class="arrow">&raquo;</span>true</td>
<td class="allow">null<span class="arrow">&raquo;</span>false<br>else<span class="arrow">&raquo;</span>true</td>
<td class="allow">null<span class="arrow">&raquo;</span>false<br>else<span class="arrow">&raquo;</span>true</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
</tr><tr>
<th>c-string!</th>
<td class="deny">ERROR</td>
<td class="allow">to c-string!</td>
<td class="deny">ERROR</td>
<td class="warn">WARNING</td>
<td class="allow">to c-string!</td>
<td class="allow">to c-string!</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
</tr><tr>
<th>pointer!</th>
<td class="deny">ERROR</td>
<td class="allow">to pointer!</td>
<td class="deny">ERROR</td>
<td class="allow">to pointer!</td>
<td class="warn">WARNING</td>
<td class="allow">to pointer!</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
</tr><tr>
<th>struct!</th>
<td class="deny">ERROR</td>
<td class="allow">to struct!</td>
<td class="deny">ERROR</td>
<td class="allow">to struct!</td>
<td class="allow">to struct!</td>
<td class="warn">WARNING</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
</tr>
<th>float!</th>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="warn">WARNING</td>
<td class="allow">to float!</td>
<td class="deny">ERROR</td>
</tr>
<th>float32!</th>
<td class="deny">ERROR</td>
<td class="allow">as float32!</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="deny">ERROR</td>
<td class="allow">to float32!</td>
<td class="warn">WARNING</td>
<td class="deny">ERROR</td>
</tr>
</table>
</div>
&sup1; Casting allowed, but integer values higher than 255 will be truncated, so beware!<br>
&sup2; Function! is a pseudo-type that cannot be the target of a type casting.
===Expressions
Expressions are the basic building blocks of a Red/System program. They are composed of:
* variables
* literal values
* function calls
* operator calls
* sub-expression in parentheses
---Formal grammar rules
The grammar rules are specified in <a href="http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form" target="_new">BNF</a> format, except when using ... to mark a definition in native language.
<literal> ::= ... any valid Red/System literal value ...
<variable> ::= ... any valid Red/System variable name ...
<logic-call> ::= ... function call that returns a value of logic! type ...
<func-call> ::= ... function call that returns a value ...
<statement> ::= ... statement or function without return value...
<logic-literal> ::= "true" | "false"
<math-op> ::= "+" | "-" | "*" | "/" | "//"
<bitwise-op> ::= "and" | "or" | "xor"
<comparison-op> ::= "=" | "<>" | "<" | "<=" | ">=" | ">"
<op> ::= <math-op> | <bitwise-op>
<cond-expr> ::= <value> <comparison-op> <expression>
<condition> ::= <logic-literal> | <logic-call> | <cond-expr>
<all> ::= "ALL" "[" <condition>+ "]"
<any> ::= "ANY" "[" <condition>+ "]"
<either> ::= "EITHER" <condition> "[" <expression>+ "]" "[" <expression>+ "]"
<short-circuit> ::= <all> | <any> | <either>
<paren> ::= "(" <expression> ")"
<value> ::= <variable> | <literal> | <paren> | "null"
<infix> ::= <value> <op> <expression>
<prefix> ::= <func-call> <expression>* | <statement> <expression>*
<call> ::= <prefix>+ | <infix> | <cond-expr>
<expression> ::= <call> | <value> | <short-circuit>
An expression can be used standalone, after an assignment or as argument to some statements (RETURN, IF or EITHER used as statement).
<u>Note</u>: EITHER can be used in an expression only if the last expressions in both its TRUE block and FALSE block are of the same type. When used in an expression, EITHER is similar to the C ternary operator (?).
<b>Examples</b>
a: 123
foo a + 1
0 < foo a + 1
any [(0 < foo a + 1) a > 0]
if any [(0 < foo a + 1) a > 0][print "ok"]
b: 1 + (2 * a - either zero? a [0][a + 100])
---Evaluation order rule
Expressions are evaluated from <b>left to right</b>. There is no operator precedence except for infix functions which do have precedence over prefix calls.
<b>Examples</b>
1 + 2 * 3 ;-- (1 + 2) * 3 returns 9
1 + 2 * 3 = 9 ;-- ((1 + 2) * 3) = 9 returns TRUE
9 = 1 + 2 * 3 ;-- ((9 = 1) + 2) * 3 raises an error!
1 + (2 * 3) ;-- 1 + (2 * 3) returns 7
foo 1 + 2 ;-- foo (1 + 2)
1 + foo 2 * 3 ;-- 1 + (foo (2 * 3))
===Functions
Function definition and usage is pretty straightforward in Red/System. The function specification block contains all the definitions required by the function. This includes:
*calling arguments
*optional returned value type
*declaration of local variables
*special attributes
\note Dialects
The specification block is a Red/System dialect (DSL), so the words inside such block are interpreted specifically and relatively to this <i>function definition</i> dialect, they are not related or affected by other namespaces, like the global variables namespace. A consequence is that using the type names as variables names is possible, but not recommended in order to keep the source code more easily readable.
/note
---Declaration
<b>Syntax</b>
<name>: func | function [
[<attributes>] ;-- optional part
<argument> [<datatype>]
...
return: [<datatype>] ;-- returned value type (optional part)
/local ;-- local variables (optional part)
<local> [<datatype>]
...
][
<body>
]
<name> : function's name
<attributes> : special attributes
<argument> : function's argument indentifier
<datatype> : integer! | byte! | logic! | pointer! [integer! | byte!] |
float! | float32! | c-string! | struct! [<members>]
<local> : local variable
<body> : function's body code
<b>Examples</b>
hello: func [][print "hello"] ;-- no arguments, no locals, no return value
why?: func [return: [integer!]][42] ;-- minimal function returning an integer
inc: func [ ;-- increment an integer
a [integer!]
return: [integer!]
][
a + 1 ;-- last value is returned
]
percent?: func [ ;-- return relative percentage of a / b
a [integer!]
b [integer!]
return: [integer!]
/local c ;-- declare local variables
][
c: 100
a * c / b
]
\note Arguments passing
In current implementation, pointer!, integer!, byte!, float!, float32! and logic! arguments are passed by value, while c-string! and struct! arguments are passed by reference.
/note
---Return value
Any function is able to return a value if necessary. This is trivially achieved as last expression in function's body will be automatically returned if:
*a RETURN: statement is present in function's spec block
*the datatype of the function ending expression matches the one declared after the RETURN: statement
\note Implementation-specific note
Some language statements like ALIAS or FUNC, are handled by the compiler in such a way that there are transparent to surrounding code. This means that such statement could be added after a function ending expression without having impact on it, nor raising any compilation error. Such construction has not much sense and would be considered bad practice if used.
/note
---Attributes
It is possible to change how the function will behave at runtime using special attributes.
+++Infix
Allow the function to be called using an infix syntax. The function must take two arguments exactly or else a compilation error will be raised. Example:
avg: func [[infix] a [integer!] b [integer!] return: [integer!]][
(a + b) / 2
]
10 avg 6
will return:
8
<u>Notes</u>:
*When the <b>infix</b> syntax is used, the postfix syntax is still allowed, but it will work only if there is no value on the left side of the function call. Example:
func [return: [integer!]][
avg 10 6 ;-- will return 8 as well
]
print "ok" avg 10 6 ;-- will produce a compilation error
*The left-to-right evaluation rule applies also for user-defined infix functions, so:
10 avg 6 + 2 ;-- avg is executed first, then +
is not equivalent to
10 avg (6 + 2) ;-- + is executed first, then avg
+++Cdecl
Changes function's calling convention to C convention. This allows to safely pass a Red/System function as argument to imported C functions.
Example:
#import [
"foo.dll" cdecl [
foo: "foo" [
fun [function! [a [integer!] b [integer!] return: [logic!]]]
return: [integer!]
]
]
]
compare: func [
[cdecl] ;-- use C calling convention
left [integer!] right [integer!]
return: [logic!]
][
left <= right
]
foo :compare ;-- pass the function pointer
\note Stdcall support
The <b>stdcall</b> attribute is also accepted, but currently as no effect, as it is already the default calling convention in Red/System.
/note
+++Variadic
Triggers the variable argument mode for native or imported functions. A native function using this attribute must provide two arguments in its specification block:
* an integer variable for the arguments count
* a pointer for the argument list (pointer! [integer!])
An imported function just needs the attribute without any other arguments declaration.
Examples of definition:
my-print: func [ ;-- native function
[variadic]
count [integer!] list [int-ptr!]
][
print ["count: " count lf]
until [
print [list/value lf]
list: list + 1
count: count - 1
zero? count
]
]
#import [ ;-- imported function
LIBC-file cdecl [
printf: "printf" [[variadic]] ;-- no need to specify any argument here
]
]
Passing arguments to a variadic function is achieved by wrapping them in a block (squared brackets delimited list).
Example of calls:
my-print ["hello" 123 "world"]
will output:
count: 3
00402035 ;-- pointer to "hello" c-string
0000007B ;-- 123 in hexadecimal
00402030 ;-- pointer to "world" c-string
Calling an imported variadic function:
printf ["%s %i %s" "hello" 123 "world"]
will output:
hello 123 world
\note Compatibity with other attributes
<b>Variadic</b> attribute can only be used alone or with the <b>cdecl</b> attribute.
/note
+++Typed
Triggers the variable argument mode with type information for native functions. <b>Typed</b> is similar to the <b>variadic</b> attribute. A native function using this attribute must provide two arguments in its specification block:
* an integer variable for the arguments count
* a pointer for the argument records (using the <b>typed-value!</b> alias)
The <b>typed-value!</b> alias is defined as
typed-value!: alias struct! [
value [integer!] ;-- argument value or pointer
type [integer!] ;-- argument type
]
Types are defined by the runtime as:
#define type-logic! 1
#define type-integer! 2
#define type-byte! 3
#define type-float32! 4
#define type-float64! 5
#define type-float! 5
#define type-c-string! 6
#define type-byte-ptr! 7
#define type-int-ptr! 8
#define type-function! 9
#define type-struct! 1000
#define any-struct? [1000 <=]
#define alias? [1001 <=]
\note Typed and imported function
It is possible to use <b>typed</b> attribute for an imported function, but it needs to meet the specific stack layout required by this attribute, so should probably be reserverd only for imported function written in Red/System.
/note
Example of definition:
vprint: func [
[typed]
count [integer!] list [typed-value!]
][
print ["count: " count lf]
until [
print [list/value " : "]
print [form-type list/type lf] ;-- to-type converts a type ID to a c-string
list: list + 1
count: count - 1
zero? count
]
]
Passing arguments to a variadic function with type information is achieved by wrapping them in a block (squared brackets).
Example of calls:
vprint ["hello" 123 "world"]
will output:
count: 3
00402043 : c-string! ;-- pointer to "hello" c-string
0000007B : integer! ;-- 123 in hexadecimal
0040203E : c-string! ;-- pointer to "world" c-string
\note Compatibity with other attributes
<b>Typed</b> attribute can only be used on its own or with the <b>cdecl</b> attribute.
/note
---Type inference
Functions offer a limited type inference possibility for local variables.
In practice, it means that it is allowed to omit a local variable type declaration as long as the variable is initialized properly.
Example:
foo: func [
a [integer!]
return: [integer!]
/local c ;-- omitted local variable type
][
c: 10 ;-- variable type is integer!
a + c
]
---Calling a function
Calling a function is achieved by writing its name followed by the required number of arguments.
<i>(from the previous examples)</i>
hello ;-- will print "hello" in the standard output
answer: why? ;-- will return 42 in variable 'answer
foo: 4
foo: inc foo ;-- foo holds 5 after the call to 'inc
bar: percent? 3 4 ;-- bar holds 75
It is also possible to pipe several function calls together:
foo: percent? 11 inc inc why? ;-- will return 25 in foo
---Function pointer
It is possible to obtain a function address to pass it, for example, as argument to external calls with callbacks.
<b>Syntax</b>
:<function name>
Example:
progress: func [[cdecl] count [integer!]][
print "." ;-- make the user see some progress
]
get-file "bigfile.avi" :progress ;-- blocking job would call 'progress
;-- periodically
<u>Note</u>: Function address is returned as a function pointer pseudo-type, so it cannot be used as-is in expressions, but it can be safely casted to an integer! if required.
---Premature exiting
Exiting at function's end is not always desirable. Sometimes, conditional premature exiting from the function is required. This can be done using EXIT or RETURN special keywords.
+++Exit
Immediatly quits the function.
test: func [a [integer!]][
if zero? a [exit] ;-- exit the function here if a = 0
... ;-- if a <> 0, continue processing...
]
+++Return
Immediately quits the function and returns a value.
test: func [
a [integer!]
return: [c-string!]
][
if zero? a [
return "Not allowed" ;-- exit the function here if a = 0
]
"ok" ;-- return "ok" if a <> 0
]
===Scoping
In Red/System, variables are statically scoped. The place in source code where a variable is declared determines its scope.
---Global Context
Global context is defined as the global namespace where all global variables and functions are defined. This context is unique. As a simple rule, every variable not declared in a function is a global variable bound to global context.
Example:
foo: 123 ;-- global variable
f: func [/local bar [integer!]][
bar: 123 ;-- locally scoped variable
]
---Functions Contexts
Each defined function has its own local context. Variables declared in function's definition block are locally scoped and can't be accessed outside of the function's body. On the other hand, global variable can be referenced and modified from functions. If a local variable has the same name as a global one, the local one will take precedence in function's body. The value of the homonym global variable won't be affected by local redefinitions in functions contexts.
Example:
foo: 1 ;-- global variable
var: 2
f: func [
return: [integer!]
/local
bar [integer!]
][
bar: 3
foo + var + bar ;-- will return 6
]
f: func [
return: [integer!]
/local
bar [integer!]
var [integer!]
][
bar: 3
var: 10 ;-- 'var is a local variable here
foo + var + bar ;-- will return 14
]
---Namespaces
It is possible to define local namespaces to provide local contexts able to encapsulate source code.
<b>Syntax</b>
<name>: context [<code>]
<name> : namespace identifier
<code> : body block of code
Any code is allowed in the body block including function definitions. All variables and functions created will have a local name that can be accessed locally or from outside using context name prefix in a path notation:
<name>/<variable> ;-- reading a variable from outside
<name>/<variable>: ;-- setting a variable from outside
<name>/<func-name> ;-- invoking a function from outside
The following elements are affected by namespace and become locally defined when declared in a context body block:
* variables
* functions
* imported functions
* enumerations
* aliases
<u>Example</u>:
b: 0
a: context [
b: 123
foo: func [/local b][
b: 1
print-line b
]
print-line b ;-- will output 123
foo ;-- will output 1
]
print-line b ;-- will output 0
print-line a/b ;-- will output 123
a/foo ;-- will output 1
a/b: a/b + 1
print-line a/b ;-- will output 124
\note Namespaces are not objects!
Even if they look like objects, namespaces only exist at compilation time, so they cannot be manipulated at run-time.
/note
+++Nested namespaces
Nesting namespaces is allowed. When conflicting names are used for variables or functions, the nearest definition is used.
<u>Example</u>:
a: context [
b: 123
c: context [
#enum colors! [red green blue]
b: "hello"
foo: func [][print-line b]
]
print-line b ;-- will output 123
c/foo ;-- will output hello
]
print-line a/b ;-- will output 123
a/c/foo ;-- will output hello
print-line a/c/b/2 ;-- will output e
print-line a/c/blue ;-- will output 2
+++Global context access
From nested namespaces, in order to access an indentifier from global context when it is also defined locally, a special virtual path is provided to solve such cases:
<b>Syntax</b>
system/words/<name> ;-- calling a function, reading a variable
system/words/<name>: ;-- setting a variable
<name> : identifer or access path
<u>Example</u>:
b: 123
a: context [
b: 0
print-line system/words/b ;-- will output 123
]
+++WITH keyword
In order to limit source code verbosity, the namespace prefix can be omitted if the source code is enclosed in a <b>with</b> body block.
<b>Syntax</b>
with <name> [<code>]
with [<names>] [<code>] ;-- alternative syntax for multiple names
<name> : namespace identifier
<names> : list of namespace identifiers
<code> : body block of code
Note: If multiple namespaces are specified and if they share same identifier(s), priority is given to namespaces that are mentioned first.
<u>Example</u>:
a: context [b: 0]
c: context [b: 1 d: 123]
with a [
print-line b ;-- will output 0
print-line c/b ;-- will output 1
]
with [a c][
print-line d ;-- will output 123
print-line b ;-- will output 0 ('a is before 'c)
]
===Infix operators
Infix operators take two arguments and are positioned between them.
---Math operators
The following math operations apply on integer or float values. When the operation results in an exceeded memory storage limit, <i>behaviour to be defined</i>.
<b>Addition</b>: +
value1 + value2
<b>Subtraction</b>: -
value1 - value2
<b>Multiplication</b>: *
value1 * value2
<b>Division</b>: /
value1 / value2
<b>Remainder</b>: %
value1 % value2
Note: a remainder will be negative if the divider is negative. Same as in C or // operator in REBOL. See float types section for specific behaviour of this operator on float values.
<b>Modulo</b>: //
value1 // value2
Note: a positive result is always returned. Same as 'modulo function in REBOL. See float types section for specific behaviour of this operator on float values.
where
value1 : an expression returning an integer!
value2 : expression of same (or compatible) datatype as <value1>
The resulting value type for math operators is the type of the left argument (an implicit type casting is operated when required).
<u>Note:</u> for + and - operators, a pointer!, struct! or c-string! value can be used for both arguments or as first argument with an integer expression as second argument (see "Pointer arithmetic").
---Bitshift operators
<b>Signed left shift</b>: <<
value1 << value2
<b>Signed right shift</b>: >>
value1 >> value2
<b>Unsigned right shift</b>: >>>
value1 >>> value2
where
value1 : an expression returning an integer! or byte!
value2 : integer! expression restricted to 0-31 range only.
<u>Note</u>: There is no unsigned left shift operator as it is the same as the signed left shift one.
---Bitwise operators
<b>Bitwise OR</b>: or
value1 or value2
<b>Bitwise XOR</b>: xor
value1 xor value2
<b>Bitwise AND</b>: and
value1 and value2
<b>Bitwise / Logical NOT</b>: not
not value1
where
value1 : an expression returning an integer!, byte! or logic!
value2 : expression of same datatype as <value1>
<u>Note</u>: Logical NOT will return the opposite of the logic argument (TRUE<=>FALSE), while bitwise NOT will apply one's complement on the integer argument.
---Comparison operators
These operators can be used only where a condition is allowed. See "Control flow functions" section for a list of functions using conditions.
<b>Equal</b>: =
value1 = value2
<b>Not equal</b>: <>
value1 <> value2
<b>Greater than</b>: >
value1 > value2
<b>Lesser than</b>: <
value1 < value2
<b>Greater than or equal</b>: >=
value1 >= value2
<b>Lesser than or equal</b>: <=
value1 <= value2
where
value1 : an expression returning a integer!, byte!, float!, float32!,
c-string!, pointer! or struct!
value2 : expression of same datatype as <value1>
<u>Note</u>:
*= and <> can also be used to compare logic! values.
*For c-string!, pointer! and struct! comparisons are done on references, not on the value pointed at.
===Control flow functions
---if
Execute a block of code if a given condition is true. IF does not return any value, so cannot be used in an expression.
<b>Syntax</b>
if <condition> [<code>]
<condition> : a conditional expression
<code> : code to execute if the condition is true
<b>Example</b>
if a < 0 [print "a is negative"]
---either
Execute a block of code if a given condition is true, else execute an alternative block of code. If last expressions in both blocks have the same type, EITHER can be used inside an expression.
<b>Syntax</b>
either <condition> [<code>][<alternative>]
<condition> : a conditional expression
<code> : code to execute if the condition is true
<alternative> : code to execute if the condition is false
<b>Examples</b>
either a < 0 [
either a = 0 [
msg: "zero"
][
msg: "negative"
]
][
msg: "positive"
]
print ["a is " msg lf]
An alternative way to write it (allowed because all code paths return a value of the same type):
msg: either a < 0 [
either a = 0 [
"zero"
][
"negative"
]
][
"positive"
]
prin ["a is " msg lf]
---until
Loop over a block of code until the condition at end of block, is met. UNTIL does not return any value, so cannot be used in an expression.
<b>Syntax</b>
until [
<code>
<condition>
]
<code> : code to execute while the condition is met
<condition> : a conditional expression
<u>Note</u>: The loop will always be executed at least once, even if the condition is not met from the beginning.
<b>Example</b>
c: 5
until [
print "o"
c: c - 1
c = 0
]
will output:
ooooo
---while
While a given condition is met, execute a block of code. WHILE does not return any value, so cannot be used in an expression.
<b>Syntax</b>
while [<condition>][<code>]
<condition> : a conditional expression
<code> : code to execute if the condition is met
<u>Note</u>: It is possible to execute any code in the condition block as long as it ends with a conditional expression.
<b>Example</b>
c: 5
while [c > 0][
print "o"
c: c - 1
]
will output:
ooooo
---any
Global condition is met if at least one of the sub-conditions is met. ANY returns a logic! value.
<b>Syntax</b>
any [<condition-1> <condition-2> ...]
<condition-*> : a conditional expression
<b>Example</b>
if any [foo > 5 bar = 0][
print "true" ;-- reached if at least one condition is met
]
---all
Global condition is met if all the sub-conditions are met. ALL returns a logic! value.
<b>Syntax</b>
all [<condition-1> <condition-2> ...]
<condition-*> : a conditional expression
<b>Example</b>
if all [foo > 5 bar = 0][
print "true" ;-- reached if both conditions are met
]
---case
Execute the block of code following the first condition that is met. If all blocks of code end with an expression of same type, then CASE can be used inside an expression. A catch-all rule can be written using a conditional expression that always results in true.
<u>Note</u>: if no value matches, a runtime error will be raised.
<b>Syntax</b>
case [<condition> [<body>] ...]
<condition> : a conditional expression
<body> : code to execute if the condition is met
<b>Examples</b>
a: 3
case [
zero? a [print "0"]
a = 1 [print "1"]
a > 2 [print "greater than 2"]
]
will output:
greater than 2
Example retrieving the returned value:
time: 8
msg: case [
all [6 < time time < 11]["morning"]
all [11 <= time time < 22]["evening"]
time >= 22 ["night"]
]
print ["Good " msg]
will output:
Good morning
---switch
Execute the block of code following the first value matched, or the default block if present and no value matched. If all blocks of code end with an expression of same type, then SWITCH can be used inside an expression.
<u>Note</u>: if no value matched and no default block is provided, a runtime error will be raised.
<b>Syntax</b>
switch <expression> [<values> [<body>] ...]
switch <expression> [<values> [<body>] ... default [<default-body>]]
<expression> : an expression resulting in byte! or integer! value
<values> : one or several integer! or byte! literal values
<body> : code to execute if one of the <values> is matched
<default-body> : code to execute if no value is matched
<b>Examples</b>
a: 2
switch a [
0 [print "green"]
1 [print "orange"]
2 [print "red"]
]
will output:
red
Example using a default block:
a: 3
switch a [
0 [print "green"]
1 [print "orange"]
2 [print "red"]
default [print "white"]
]
will output:
white
Example retrieving the returned value:
a: 2
year: switch a [
0 [2010]
1 [2011]
2 [2012]
]
print ["Red will rock in " year]
will output:
Red will rock in 2012
Example using multiple values for matching:
input: 3
switch input [
0 2 4 6 8 [print "even"]
1 3 5 7 9 [print "odd"]
]
will output:
odd
===Stack functions
---push
Push a value on top of execution stack. Stack pointer is modified.
<b>Syntax</b>
push <value>
<value>: expression of any type
<b>Examples</b>
push 123
push a
push "hello"
push p/value
---pop
Pop a value from top of execution stack. Stack pointer is modified.
<b>Syntax</b>
pop
return: an integer value
===Debugging functions
---assert
Make a runtime assertion. If assertion fails, a runtime error will be raised.
<b>Syntax</b>
assert <conditional expression>
<b>Example</b>
Red/System []
assert 1 = 2
will produce if saved to %test.reds file and run:
*** Runtime Error 98: assertion failed at line 3
*** in file: %test.reds
\note Compiler debug mode
Assertion will only be compiled if the <b>debug?</b> compilation option is set to YES or if the <b>-g</b> command-line option is used, else the assertion will be ignored by the compiler.
/note
===System structure
System structure is a special struct value, defined at run-time, that gives access to some core features of Red/System.
---args-count
Informs about the number of words passed on command-line. The executable itself is included in the count, so <b>args-count</b> is greater or equal to one.
<b>Syntax</b>
system/args-count
return: an integer value (>= 1)
---args-list
Pointer to an array of words passed on command-line (including the program name). The array's end is marked by a null pointer.
<b>Syntax</b>
system/args-list
return: a pointer value of type: str-array! (alias)
The pointer alias is defined as:
str-array!: alias struct! [
item [c-string!]
]
<b>Example</b>
Red/System [purpose: "demo system/args-* info usage"]
print ["count: " system/args-count lf]
args: system/args-list
c: 1
until [
print [c ": " args/item lf]
c: c + 1
args: args + 1
args/item = null
]
If compiled as <i>show-args</i> executable, and called as:
show-args 123 -p hello
it would output:
count: 4
1: show-args
2: 123
3: -p
4: hello
---env-vars
Pointer to an array of shell environment variables with their value. The array's end is marked by a null pointer.
<b>Syntax</b>
system/env-vars
return: a pointer value of type: str-array! (alias)
The pointer alias is defined as:
str-array!: alias struct! [
item [c-string!]
]
<b>Example</b>
Red/System [purpose: "demo system/env-vars usage"]
env: system/env-vars
until [
print [env/item lf]
env: env + 1
env/item = null
]
would output (specific to your shell/OS):
ORBIT_SOCKETDIR=/tmp/orbit-root
SSH_AGENT_PID=2248
TERM=xterm
SHELL=/bin/bash
... ;-- rest of output omitted
\note Implementation note
On Windows platforms, system/env-vars is always set to null. This may change in future.
/note
---stack/top
Set or retrieve execution stack top address.
<b>Syntax</b>
Getting stack top value:
system/stack/top
return: a pointer value of type: pointer! [integer!]
Setting stack top value:
system/stack/top: <address>
<address>: a pointer value of type: pointer! [integer!]
\note Stack pointer modification
Do not use it unless you know exactly what you are doing, else your program will most probably crash.
/note
---stack/frame
Set or retrieve execution stack frame address.
<b>Syntax</b>
Getting stack frame value:
system/stack/frame
return: a pointer value of type: pointer! [integer!]
Setting stack frame value:
system/stack/frame: <address>
<address>: a pointer value of type: pointer! [integer!]
\note Stack pointer modification
Do not use it unless you know exactly what you are doing, else your program will most probably crash.
/note
---pc
Retrieve the CPU program counter value.
<b>Syntax</b>
system/pc
\note IA-32 target
This access path will return the value of the EIP register.
/note
---alias
Get the ID value of an alias. This is required in typed functions in order to distinguish different struct! values.
<b>Syntax</b>
system/alias/<name>
where
<name> : existing alias name
This expression returns an <b>integer!</b> value that can be used to test at runtime, the type of an argument in a typed function.
Example:
foo!: alias struct! [a [byte!]]
s: declare foo!
probe: func [
[typed] count [integer!] list [typed-value!]
][
until [
if list/type = system/alias/foo! [
print "foo! alias detected"
]
list: list + 1
count: count - 1
zero? count
]
]
probe [1 "r" s 123]
will output:
foo! alias detected
===Compiler directives
Some features of Red/System need to be processed at compile-time rather than at run-time. This is especially true for features related to the linking phase that builds the executable file. In order to distinguish such compile-time commands or options, compiler directive are introduced. Their syntax is:
#<directive> <argument-1> <argument-2> ...
<directive> : a valid identifier
<argument-*> : argument can be any Red valid datatype
The number of arguments is specific to each compiler directive.
As compiler directives apply globally to programs, they are not allowed inside code blocks (this restriction might be removed if local directives are introduced in the future).
<u>Implementation note</u>: The directive arguments datatype set, is the one provided by REBOL during the bootstrapping phase only. Once the Red layer has been implemented, the allowed datatypes will be Red ones.
===Importing External Libraries
---#import
Red/System is able to load external shared libraries at the time a Red/System executable is loaded by the operating system. This requires that the programmer gives instructions to the compiler about which library to load and how to map library's functions to Red/System global context. This feature is called "library import" in Red/System and it is supported by a specific compiler directive: <b>#import</b>. This directive can be used anywhere in the global context of your Red/System program, but pay attention to put it <i>before</i> you use one of the mapped functions, else a compiler error will be raised. This directive can be used multiple times in your source code if it makes it more readable. If there is a huge number of functions to imports, putting them in separate includes files would be considered as good practice.
<u>Note</u>: this is not the same as dynamically loading a shared library from your Red/System code after your program has started. Such approach allows you to delay the loading of your libraries and to free them. Imports cannot be freed.
<b>Syntax</b>
#import [
"<library>" <convention> [
<function name>: "<ID>" [
<argument> [<datatype>]
...
return: [<datatype>] ;-- optional part
]
... ;-- more functions mappings
]
... ;-- more libraries to load
]
<library> : shared library file name (with extension)
<convention> : calling convention of the library (stdcall | cdecl)
<function name> : name of the mapped function in global context
<ID> : identifier of the function in the shared library
<argument> : function's argument indentifier
<datatype> : integer! | byte! | pointer! [integer! | byte!] |
float! | float32! | c-string! | struct! [<members>] |
function! [<specification>]
<u>Notes</u>:
*An absolute path to the library can be provided in OS-specific format.
*The <b>RETURN:</b> statement indicates that the mapped function has a return value.
*There's no limitation on the number of libraries or functions that can be declared this way.
Supported calling conventions are:
*<a href="http://en.wikipedia.org/wiki/X86_calling_conventions#stdcall" target="_new">stdcall</a>: used mostly by Windows API (but can be also used by third-party DLLs)
*<a href="http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl" target="_new">cdecl</a>: default calling convention used by C shared libraries.
<b>Usage</b>
The following example is Windows-specific.
#import [
"kernel32.dll" stdcall [
process-id?: "GetCurrentProcessId" [
return: [integer!]
]
get-env: "GetEnvironmentVariableA" [
name [c-string!]
buffer [c-string!]
size [integer!]
return: [integer!]
]
]
"msvcrt.dll" cdecl [
malloc: "malloc" [
size [integer!]
return: [c-string!]
]
free: "free" [
block [c-string!]
]
]
]
pid: process-id?
max-size: 128
buf: malloc max-size ;-- allocate space for 127 characters
get-env "windir" buf max-size
print buf
free buf
would output:
C:\Windows
---#syscall
As Red/System is destined to be used mostly for low-level system programming, <a href="http://en.wikipedia.org/wiki/System_call" target="_new">syscalls</a> mappings are also supported using the <b>#syscall</b> compiler directive.
<b>Syntax</b>
#syscall [
<function name>: <ID> [
<argument> [<datatype>]
...
return: [<datatype>] ;-- optional part
]
... ;-- more functions mappings
]
<function name> : name of the mapped function in global context
<ID> : syscall integer ID
<argument> : syscall's argument indentifier
<datatype> : integer! | byte! | pointer! [integer! | byte!] |
float! | float32! | c-string! | struct! [<members>]
The <b>RETURN:</b> statement indicates that the mapped syscall has a return value.
There's no limitation on the number of syscalls that can be declared this way.
<b>Usage</b>
The following example is Linux-specific, but should work with most UNIX systems.
#syscall [
write: 4 [
fd [integer!] ;-- file descriptor, STDOUT = 1
buffer [c-string!]
count [integer!]
return: [integer!]
]
quit: 1 [ ;-- "exit" syscall, no return value
status [integer!]
]
]
msg: "Hello World"
result: write 1 msg length? msg
if negative? result [
print "Error: write failed"
quit 3 ;-- exit and return an error code
]
quit 0 ;-- no error
will output (if no error):
Hello World
===Source Processing
Red/System relies on a preprocessor to make compile-time modifications of the source code in order to provide syntactic sugars, like hexadecimal and character literal forms for integers. Some features are user controlled through compiler directives like <b>#define</b> or <b>#include</b>.
---#define
The <b>#define</b> compiler directive is a rudimentary macro system that can be used to:
*define constant values
*make simple macro expressions
The name matching method is exact word matching. This ensures that no accidental source code corruption can occur.
<b>Syntax</b>
#define <name> <value>
<name> : identifier to use in the source code
<value> : single value or block of values to replace in the source code
<b>Usage</b>
#define R_PART 00FF0000h ;-- simple constants definitions
#define G_PART 0000FF00h
#define B_PART 000000FFh
#define zero? [0 =] ;-- simple test expression macro
color: 00550063h
if zero? (R_PART and color) [
print "Red not found"
]
if zero? (G_PART and color) [
print "Green not found"
]
if zero? (B_PART and color) [
print "Blue not found"
]
will output:
Green not found
<u>Note</u>: Parens are required in this example on test expressions so that the compiler performs the second infix expression (the and operator) before the first (the equal operator).
---#enum
The <b>#enum</b> compiler directive allows to declare enumerations using labels. A list of integers is assign to a list labels. These labels could then be used anywhere in the source code and will be converted to their integer value when needed by the compiler.
<b>Syntax</b>
#enum <name>! [
<label> | <label>: <value>
...
]
<name>! : enumeration name (ending with a ! by convention)
<label> : label to which an integer value is assigned to
<label>: : one or several label(s) set to a given integer value
<value> : integer value
The enumeration starts from 0 by default. It is possible to assign a starting value using a label name with a ending colon, followed by an integer. It is also possible to assign several labels to the same value.
<u>Notes:</u>
* labels syntactic rules are the same as for variable identifiers.
* enumeration names are pseudo-types that can be used in any local or global variable declaration in place of a real type.
* labels can be used in place of integer variables or literal integer values.
* ambiguous enum or label names that could conflict with other existing names will raise a compilation error.
<b>Examples</b>
#enum colors! [A B C D E]
print-wide [A E]
will output
0 4
Using in place of literal integers:
#enum colors! [red blue green yellow]
a: red
print switch a [
red ["Red"]
blue ["Blue"]
yellow ["Yellow"]
]
will output
Red
Defining labels with user-selected value:
#enum values! [a: 1 b c d: e: 10]
print-wide [a b c d e]
will output
1 2 3 10 10
---#include
The <b>#include</b> compiler directive will insert the target source file at the current position in the calling source code.
This directive helps split the source code in several files, allowing for example, to put common functions or definitions in a single place and including them where required, across several source files or across different projects.
<b>Syntax</b>
#include %<file>
<file> : relative or full path to a Red/System source file
\note Filename format
The filename must be written using the OS-independent <a href="http://www.rebol.com/docs/core23/rebolcore-12.html#section-2" target="_new">REBOL file! format</a>.
/note
<b>Usage</b>
Rewriting the example from <i>#define</i> section:
definitions.reds file:
#define R_PART 00FF0000h
#define G_PART 0000FF00h
#define B_PART 000000FFh
#define zero? [0 =]
test-primary: func [
color [integer!]
mask [integer!]
msg [c-string!]
][
if zero? (color and mask) [
print msg
]
]
main.reds file:
#include %definitions.reds
color: 00550063h
test-primary color R_PART "no Red found"
test-primary color G_PART "no Green found"
test-primary color B_PART "no Blue found"
---#if
The <b>#if</b> compiler directive goal is to allow conditional compilation based on a simple conditional expression limited to a few compilation options. If the expression result is TRUE, the following block of code is compiled.
<b>Syntax</b>
#if <option> <op> <value> [
<body>
]
<option> : compiler option name (case-insensitive)
<op> : =, <>, <, >, <= or >=
<value> : any value accepted by the property
<body> : Red/System source code
Valid option names and allowed values are listed in %config.r file, in a comment section. For words value, both word! and lit-word! syntaxes will be accepted.
<b>Example</b>
#if OS = 'Windows [
print "Running on Windows"
]
<u>Note</u>: if the argument is a word and not a value (like an integer! or a logic!), it needs to be prefixed with ' character to avoid being evaluated (as in normal REBOL expressions).
---#either
The <b>#either</b> compiler directive allows conditional compilation based on a simple conditional expression limited to a few compilation options. If the expression result is TRUE, the first block of code is compiled, else the second one is compiled.
<b>Syntax</b>
#either <option> <op> <value> [
<body-TRUE>
][
<body-FALSE>
]
<option> : compiler option name (case-insensitive)
<op> : =, <>, <, >, <= or >=
<value> : any value accepted by the property
<body-*> : Red/System source code
Valid option names and allowed values are listed in %config.r file, in a comment section. For words value, both word! and lit-word! syntaxes will be accepted.
<b>Example</b>
#either OS = 'Windows [
print "Running on Windows"
][
print "Running probably on a UNIX platform"
]
<u>Note</u>: if the argument is a word and not a value (like an integer! or a logic!), it needs to be prefixed with ' character to avoid being evaluated (as in normal REBOL expressions).
---#switch
The <b>#switch</b> compiler directive allows to define several conditionally compiled block of codes, depending on the value of a compilation option. The block following the option name is searched for a matching value and the following block of code is compiled.
<b>Syntax</b>
#switch <option> [
<value-1> [
<body-1>
]
<value-2> [
<body-2>
]
...
#default [ ;-- optional default clause
<body-N>
]
]
<option> : compiler option name (case-insensitive)
<value-*> : any value accepted by the property
<body-*> : Red/System source code
Valid option names and allowed values are listed in %config.r file, in a comment section. For words value, both word! and lit-word! syntaxes will be accepted.
<u>Note</u>: the #default clause is optional. If present, it acts as a default catch-all value if none other is matched.
<b>Example</b>
#switch type [
exe [print "Building an executable"]
dll [print "Building a dynamically linked library"]
obj [print "Building an object file"]
lib [print "Building a statically linked library"]
]
<u>Note</u>: if the argument is a word, it does not need to be prefixed with ' character in a #switch block.
===Source code organization
---Source files suffix
The official Red/System source file suffix to use is: <b>.reds</b>
---Header
Red/System enforces the usage of a standard header for all sources (one of the great ideas in REBOL), to both identify a valid Red/System program and document it.
<b>Syntax</b>
A valid Red/System source file will need this header:
Red/System [
<name>: <value>
... ;-- more attributes...
]
<name> : valid identifier
<value> : any Red valid datatype
There is no minimum or maximum number of entries that a valid header can contain, so an empty block will also be valid (but bad practice).
<u>Implementation note</u>: Header values types are the ones provided by REBOL during the bootstrapping phase only. Once the Red layer will be implemented, the allowed datatypes will be Red ones.
The attribute that you can specify are not limited, you can add whatever you want/need. Anyway, some attribute names are used by convention:
*<b>Title</b>: application title
*<b>Purpose</b>: short description of the application purpose
*<b>Author</b>: source code author name
*<b>File</b>: name of the source file
*<b>Version</b>: source code version (usually using a <a href="http://www.rebol.com/docs/core23/rebolcore-16.html#section-3.9" target="_new">tuple! literal</a>)
*<b>Date</b>: date of last version
*<b>Rights</b>: copyrights
*<b>License</b>: source license (URL or full text)
*<b>History</b>: source modifications history
*<b>Note(s)</b>: any special notice
<b>Example</b>
Red/System [
Title: "Red/System small demo app"
Author: "Nenad Rakocevic"
File: %hello.reds
Rights: "Copyright (C) 2011 Nenad Rakocevic. All rights reserved."
License: "BSD-3 - https://github.com/dockimbel/Red/blob/master/COPYING"
]
---Code flow layout
A typical Red/System program is a mix of function definitions and global code (meaning executable code that is not in a function). There is no concept of "main" function in Red/System. The only entry point is the beginning of the source code and the exit point is at the end of the source code, or at a QUIT call if encountered before.
Example:
foo: 123
print "hello"
bar: func [a [integer!]][foo * 2]
foo-twice: bar foo
either foo < 100 [
print "less than 100"
][
print "more than 100"
]
bye: func [][print "goodbye"]
bye
So, it is possible to mix functions and global code providing that functions are defined before they are called from global context. Such restrictions don't apply if the call is made from a function context, so cross-references like these:
foo: func [...][...bar...]
bar: func [...][...foo...]
can be processed without issues.
---Coding guidelines
<i>TBD</i>
===Integration in Red
As a dialect of Red, Red/System code will be callable from Red source code directly using one of these two methods:
<b>At runtime</b>: using the <b>DO</b> function with refinement:
do/system [...Red/System code...]
<b>At compile-time</b>: using a Red compiler directive:
#system [...Red/System code...]
\note Red/System functions export
It will be possible to export some Red/System functions to be callable from the Red language layer. The method for such export hasn't been decided yet. Among possible options, it can be:
*A special attribute in header that will list the functions to export
*A function's attribute in the specification block
*A compiler directive
/note
===Reserved keywords
The list of following symbols and keywords are reserved, they cannot be used as variable or function name:
% & *
+ - -**
/ // ///
< << <=
<> = >
>> >= >>>
alias all and
any as assert
case comment context
declare either exit
false func function
if not or
pop push return
size? switch true
until while with
xor
\note Bootstrap constraints
Both -** (internal representation for >>>) and /// (internal representation for %) are required to workaround REBOL's lexical scanner limitations. Those symbols will be removed from the reserved keywords list once the bootstrap phase will end.
/note
===Possible Evolutions
---Variables
*Add support for multiple assignments, like a: b: c: 0
---Pointers
*<strike>Remove pointer! datatype (struct! is able to do the same job)</strike> <i><b>REJECTED</b></i>
*Accept boolean operations on pointers: OR, XOR, AND (nice but use-cases would be rare?)
---Struct
*Add support for specifying struct memory alignment and padding. Default structure and members alignment would be the one specified in target object (per target). Per struct specific rules should be possible using the following syntax:
#align <integer> ;-- change memory alignment for all subsequent
;-- c-strings, pointers and structs.
struct [
[align <n> <little|big>] ;-- change members alignment and endianess
<members>
]
<n> : number of bytes to align members to
<little> : little endian (optional)
<big> : big endian (optional)
*Add support for passing struct! by value when required. Possible syntax could be:
struct [
[by-value] ;-- 'by-value keyword would force passing it
<members> ;-- by value.
]
---C-strings
*Add a FOREACH control flow function to traverse c-strings (or even array! values):
A simple way to traverse a c-string could be:
foo: "I am a c-string"
foreach c foo [prin c]
will output:
I am a c-string
---Logic!
*Add logic! support for OR, XOR, AND operators (if it provides any advantage over ANY/ALL).
---Integer!
*Bind the integer! type to int32! or int64! depending on the target platform. It needs some further investigations to determine if it can be a real advantage or not.
---Functions
*Add support for functions refinements (same as in REBOL)
*<strike>Infer functions return value datatype</strike>
---New datatypes
*<b>Array!</b>: this datatype would allow declaring arrays of values that could be accessed with an integer index (similar to C arrays). Redundancy with c-string! datatype would need to be considered. Here is a draft of possible syntax and usage:
---- C ------ --------- Red/System ----------
int p[20] = 0; p: array [20 integer!]
p[4] = 123; p/4: 123
p[i] = 123; p/i: 123
*<strike>Add a uint8! (or char!) datatype</strike>
*Add a uint16! (or short!) datatype
*Add a uint64! (or long!) datatype
*<strike><b>Binary!</b>: this datatype was reserved early in the compiler's datatypes list, but not implemented. Its purpose was just to provide literal input/output forms in hexadecimal for c-string values. A standalone datatype for such purpose might be avoidable, hence the delayed implementation.</strike> <i><b>REJECTED</b></i>
*<strike><b>Logic!</b>: add a boolean datatype, so that booleans resulting from conditional expressions become first class citizens.</strike>
---New functions
*Add a FORM function (convert any datatype to c-string!)
*<strike>Add a SWITCH function: branch on different code blocks depending on a input value</strike>
*Add a INLINE function to inline machine code: inline #{1234...} and/or assembler
*<strike>Add a NOT function that would return the boolean opposite of argument value. As booleans are not really supported in the current specs, NOT addition is postponed.</strike>
*Add a REPEAT function to be able to loop with a counter:
total: 0
repeat c 10 [total: total + c]
*Add a TYPE? function returning the argument's type as integer! (should use defines to name them)
---Misc
*Add a module system (per-file contexts for example)
*<strike>Support 0 and 1 as valid boolean results</strike> <i><b>REJECTED</b></i>
*<strike>Extend the get-word! syntax to integer! variables.</strike>
*Support multiple nested type castings.
===Document History
*<b>19/08/2012 - revision 35</b>
*>Added namespaces description
*>Added CONTEXT and WITH to keywords list.
*<b>25/07/2012 - revision 34</b>
*>Added description for variable pointer (get-word extended).
*>Removed items already implemented from "Possible Evolutions" section.
*<b>26/02/2012 - revision 33</b>
*>Removed misleading float32! syntax description and replaced by a simple explanation on how to form float32! literal values.
*>Fixed code example in section 4.4.2, the `as float32!` type casting was missing.
*<b>04/02/2012 - revision 32</b>
*>Added float! and float32! datatypes.
*>Type casting matrix updated with float! and float32!
*>Updated list of runtime type IDs.
*>Added #enum description
*<b>03/01/2012 - revision 31</b>
*>Completed CASE description
*>Added SWITCH description
*>Added SWITCH to keywords list
*>Changed the casing of the function names used as section headers.
*<b>29/12/2011 - revision 30</b>
*>Added CASE description.
*>Update keywords list.
*>Minor typo fixed.
*>"API Reference" level removed, all sub-sections are moved up and placed after "Scoping" chapter.
*>Literal struct members default zero value mention added.
*<b>21/09/2011 - revision 29</b>
*>Added description for system/alias/... special path.
*<b>20/09/2011 - revision 28</b>
*>Typed attribute ID values and macros updated.
*>Callback attribute removed and replaced by 'cdecl attribute.
*<b>17/09/2011 - revision 27</b>
*>Added missing bitshift operators in reserved keywords list.
*>Improve variadic function names in examples to avoid confusion with C function names.
*<b>01/09/2011 - revision 26</b>
*>Added system/pc description.
*<b>12/08/2011 - revision 25</b>
*>Fixed invalid hex values in some code examples.
*>Minor code example improvement for typed attributes.
*>Minor editing changes
*>Updated examples to use variadic print function
*<b>09/08/2011 - revision 24</b>
*>Typeinfo function attribute renamed to typed.
*>Added a note about imported library path format.
*>Special 'system structure fully documented
*>Added push and pop as reserved keywords
*<b>08/08/2011 - revision 23</b>
*>Variadic and typeinfo function attributes added.
*>Get-stack and set-stack functions removed.
*>Added system/stack/* descriptions.
*<b>04/08/2011 - revision 22</b>
*>Stack manipulation functions added.
*<b>24/07/2011 - revision 21</b>
*>Pointers arithmetic extended to allow addition and subtraction of pointer arguments.
*<b>07/07/2011 - revision 20</b>
*>Added ASSERT keyword description
*<b>05/07/2011 - draft 19</b>
*>Fixed #if and #either examples. Note added about lit-words arguments
*>Delimiters and free-form syntax descriptions added
*>Various small fixes and cleanups
*<b>04/07/2011 - draft 18</b>
*>Added a note about alias names living in their own namespace.
*>Added % operator and notes to help distinguish % and // operators.
*>Added a mention for global variables initialization restriction in Null description.
*>Added -**, >>>, %, /// in reserved keywords list.
*>Added bitshift operators section.
*>Propagated "struct" and "pointer" replacement by "declare <type>"
*<b>23/06/2011 - draft 17</b>
*>Updated type casting description to match the new relaxed syntax implemented.
*>Added null keyword description.
*>Upgraded pointer! type reference to take byte! into account
*>C void pointers now mapped to pointer! [byte!] and byte-ptr!
*<b>22/06/2011 - draft 16</b>
*>Added missing 'comment word in keywords list
*>Added variable initialization at root level restriction description.
*>Note added to warn about transparent statements after a function ending expression.
*>Minor code example cleanup (local type declaration removed)
*>Added a function! column in the type casting matrix.
*>Added missing Expression chapter.
*<b>12/06/2011 - draft 15</b>
*>Added #either directive
*>#if and #either now accept any comparison operator
*<b>11/06/2011 - draft 14</b>
*>Added #if and #switch directives description
*<b>07/06/2011 - draft 13</b>
*>Keywords list updated, only symbols and keywords built in the compiler are retained.
*>Function pointer description and example updated wrt recent implementation changes.
*>Added & as a reserved keyword (for future use)
*>Keyword list updated and cleaned up
*>Fixed non-confirming hex literals in examples (thanks Kaj)
*>Minor fixes
*<b>05/06/2011 - draft 12</b>
*>Callback description rewrote entirely to match the new behaviour.
*>Function! type added to imported functions specification rules
*<b>04/06/2011 - draft 11</b>
*>Multi-line comments restriction added.
*>Compiler directives usage inside code blocks is now forbidden explicitly.
*>Multiple type castings explicitly forbidden and added as possible evolution.
*>Type inference for return value removed.
*>Added new function attribute: callback
*<b>23/05/2011 - draft 10</b>
*>Added type casting combinations matrix
*>Fixed uncomplete code example for ALIAS (thanks to Kaj)
*>Fixed list of allowed characters in variables (<a href="https://github.com/dockimbel/Red/issues/48">issue #48</a>)
*>Minor other fixes
*<b>20/05/2011 - draft 9</b>
*>Added missing type casting syntax description for compound types (thanks to Andreas).
*<b>19/05/2011 - draft 8</b>
*>Added logic! datatype to struct members and type casting allowed types.
*>Added a note about evaluation rule for user-defined infix functions.
*>Added missing types supporting math and comparison operations.
*>Explained condition permitting that EITHER function be used inside expression.
*>Mention about not possible inclusion in expressions for IF, UNTIL, WHILE.
*>Returned type for ANY and ALL precised.
*<b>05/05/2011 - draft 7</b>
*>Removed mentions for c-string variables acting as constants.
*<b>27/04/2011 - draft 6</b>
*>Added 'infix attribute support in functions spec block.
*>Added missing logic! datatype in functions spec block definition.
*<b>15/04/2011 - draft 5</b>
*>Letters in hex integers are restricted to uppercase only. Warning added for variable names that could be mistaken for hex integers.
*>Logic! support for OR, XOR, AND operators withdrawn from specifications and put in the "Possible Evolution" section. (They can be replaced by ANY/ALL/NOT to compose logic expressions)
*>'Comment added as reserved keyword
*>Added a note for = and <> operators for use with logic! values. C-string! values comparisons restricted to those two operators only (c-string! was wrongly allowed for all comparison operators).
*<b>10/04/2011 - draft 4</b>
*>Struct arguments are passed by reference now again. STRUCT returns a reference to the struct value.
*>Syntax now precised for STRUCT keyword followed by an alias name
*>Struct! arithmetic added
*>C-string! arithmetic added
*>Pointer! now restricted to [integer!] only. It is more consistent with c-string! and struct! which are both (implicit) pointers already.
*>Pointer! section moved after c-string! and struct! (because it is less important now)
*<b>08/04/2011 - draft 3</b>
*>Added get-word! syntax for getting struct variable address
*>New Possible Evolution: extend get-word! syntax to all variable types
*>Added "pointer" as reserved keyword
*>Renamed "alias-type" keyword to "alias"
*>Byte! added to <datatypes> definitions
*>Pointer syntax and declaration improved and extended to support paths with indexes
*>String! type renamed to c-string!
*>C-string path accesses now uses byte! values for reading and writing
*>Added type inference for functions local variables and return value
*>Function address can be returned using get-word! syntax
*>New "Nested functions" section added to be completed later
*>Added byte! datatype, character specific syntax moved from integer! to byte!
*>Added logic! datatype (boolean values)
*>Added TRUE and FALSE keywords
*>Added NOT operator
*>Old "Logical" operators renamed to the more appropriate "Bitwise" operators
*>Bitwise ops completed with boolean counterparts
*>More accurate syntax description for all infix operators
*>Functions local variables type is now optional if the variables are properly initialized
*>Added an empty "Type Conversion" to be completed later
*>Removed functions and macros defined in runtime from keywords, they can be redefined by user code if required
*>New Possible Evolution: integer! as a platform-specific type
*>Minor corrections and additions
*<b>29/03/2011 - draft 2</b>
*>Added missing array! datatype proposition in Evolutions
*>Added missing EXIT and RETURN in reserved keywords list
*<b>28/03/2011 - draft 1</b>
*>First public release
###
REBOL []
do/args %makedoc2.r 'load-only
doc: scan-doc read file: system/options/script
set [title out] gen-html/options doc [(options)]
file: last split-path file
replace file ".txt" ".html"
file2: copy file
insert find file2 "." "-light"
replace out "$DARK$" file
replace out "$LIGHT$" file2
write file out
replace out "dark.css" "light.css"
write file2 out
Jump to Line
Something went wrong with that request. Please try again.