D2 is a strongly-typed, statically-typed, inferred-type compiled language. Its syntax draws from C, Java and Python.
D2 keywords are case insensitive, but by tradition are written in UPPER CASE. D2 variables are case sensitive.
The following types are built-in:
BOOL
: boolean (TRUE
,FALSE
are the two built-in values)BYTE
: 8-bit integerINT
: 32-bit integerLONG
: 64-bit integerDOUBLE
: 64-bit floating pointSTRING
: immutable sequence of characters. There is no separate "character" type (similar to Python)RECORD
: user-defined structure (akin to Cstruct
)- Arrays of any of the above types (except array)
NULL
: an un-set value that can be used in place of aSTRING
,RECORD
or array
When defining or using an array, RECORD
or PROC
, "forward references" (reference
before definition) are allowed.
All arithmetic and expressions must be type-consistent. There is no implicit (or explicit)
conversion between arithmetic types, though you can use
C Standard Library
functions to do so. E.g., to convert from DOUBLE
(equivalent to a 64-bit C
double
) to INT
(32 bits) you can use lround
.
BYTE
constants can represent -128 to +127, via a leading 0y
and hexadecimal.
Example: 0y2f
for 47 decimal.
INT
constants can represent -2^31-1 to 2^31. Example: 1234
LONG
constants can represent -2^63-1 to 2^63. Example: 1234L
DOUBLE
constants must include a decimal point (dot). Example: 123.4
STRING
constants can be created with single or double quotes. The empty STRING
is permitted.
STRING
s are immutable, but can be appended (a new STRING
will be returned.)
To define:
a: int[3] // declares & allocates an array in memory
a[0] = 1
a[1] = 2
a[2] = 3
// print a[3] gives runtime exceptoin
Array literals:
lit = [1, 2, 3] // also allocates memory
Array literals can be used inline as well.
Use the LENGTH
function to find the length of an array:
x = length([1, 2, 3])
println x == 3
Arrays cannot be concatenated but they can be compared using
==
and !=
. Two arrays are ==
if every element is ==
.
To define:
r: record {
f1: string
f2: int
f3: r // "forward" reference
}
To instantiate a RECORD
use the NEW
keyword:
ar = new r
ar.f1 = 's'
ar.f2 = 3
ar.f3 = new r
All fields default to their default value (zero for
all numeric types, FALSE
for BOOL
, and NULL
for others)
when the RECORD
is instantiated.
RECORD
s can be compared using ==
and !=
. Two records
are ==
if every field is ==
, but not recursively. At this
time, D2's record comparison is "shallow".
Variables do not need to be declared before assignment. You can still declare variables before use if you want:
a: int
b: string
c: r // RECORD type, either previously or subsequently defined
d: bool
Variable assignments and references must match their declared types throughout their lifetimes. If not explictly declared, whatever type is used when it is first assigned will be its type throughout its lifetime.
a = 3
// a = "hi" // compile-time error beause 'a' is forever an INT
// b = a == true // compile-time error because INT and BOOL cannot be compared.
A global variable can be defined outside any procedure and can be of any type.
A local variable can be declared or assigned inside a procedure.
a = 3 // global definition
f: proc {
a: bool // declares a local "a"
a = true
}
+
,-
,*
,/
: for BYTE, INT, LONG, DOUBLE%
: modulo for BYTE, INT, LONG|
,&
,^
: bitwise "or", "and" and "xor" for BYTE, INT, LONG>>
: shift right for BYTE, INT, LONG<<
: shift left for BYTE, INT, LONG
And all typical comparisons: ==
!=
<
<=
>
>=
.
AND
boolean andOR
boolean orNOT
boolean notXOR
boolean xor
Booleans can be compared using ==
!=
<
<=
>
>=
. By definition, TRUE > FALSE
.
STRING
constants can be created with single or double quotes. The empty STRING
is permitted.
STRING
s are immutable, but can be concatenated via the +
operator, which
usually creates a new string in memory. A single-character substring (via [index]
) may or may
not create a new STRING
.
Examples:
s = 'string'
s2 = "another string"
s3 = s + s2
s4 = s3[0]
STRING
s can be compared using the usual operators ==
!=
<
<=
>
>=
.
Two STRING
s are ==
if all their characters are ==
. Inequality (comparisons)
use the same semantics as C's strcmp
.
Use ASC
to get the ASCII INT
value of the first character of a STRING
, and CHR
to
create a 1-character STRING
from the ASCII INT
argument. (D2 does not support
Unicode yet.)
s = 'Ab'
a = asc(s) // 65
b = chr(a) // a one-character string 'A'
To find the length of a STRING
, use the built-in function LENGTH
.
println length("abc") // prints 3 with newline
See above.
Use [index]
to retrieve or set a single entry from an array. Array indices must be INT
s.
Statements can live inside or outside a procedure, like in Python.
Global statements are excuted top to bottom in a D2 file.
Typical Python semantics:
if i == 0 { // must be a boolean expression
println "zero"
} elif i == 1 {
println "one"
} else {
println "other"
}
Any number of ELIF
clauses are allowed. Like Python (and unlike Java and C),
parentheses are not required around the boolean expression.
As in Python, there is no CASE
or SWITCH
statement in D2.
i=0 while i < 10 do i++ {
// loop statements
println i
}
BREAK
and CONTINUE
work the same as in Java, C, JavaScript, Python, etc.
Use EXIT
to terminate the program with return code 0.
EXIT "Message"
prints the message to stdout and exits the program with return
code -1.
PRINT
prints a variable to stdout. There is limited built-in support for printing
arrays. There is no built-in support for printing RECORD
s.
PRINTLN expression
appends a newline after printing the value.
Reads a string from stdin until EOF, or 1 MB of data is read. (D2 is still a toy language in some ways.)
in: string
in = input
Command-line arguments are provided in the built-in ARGS
array (a 1-dimensional
array of STRING
s.)
This example defines a PROC
that takes two parameters: a STRING
and an
array of STRING
s, and returns an INT
:
index: proc(arg1: string, arg2: string[]): int {
return 0
}
VOID
PROC
s and no-arg PROC
s are supported:
think: proc { // parentheses are optional for no-arg proces
// no return statement
}
// this is the same thing:
thinkv: proc: void {
}
Just like in C, Java, Python, JavaScript, etc.
x = index('hi', ['a', 'b', 'c'])
It is legal to ignore the return value of a PROC
:
index('hi', ['a', 'b', 'c'])
To reference a procedure in the C Standard Library, prepend EXTERN
before the PROC
keyword, and don't define the body of the
procedure. Example:
srand: extern proc(n: int)
rand: extern proc: int
The procedure must be a procedure/function in the C Standard Library.
There are runtime checks in place for
NULL
pointer dereferences (STRING
s,RECORD
s, arrays)- index out of range for
STRING
and array references - division by zero and modulo zero
When possible, the compiler will discover these at compile time.