690 lines
16 KiB
Plaintext
690 lines
16 KiB
Plaintext
|
|
|
|
################# ############ ###### ######
|
|
## ## ## ## ## ## ## ##
|
|
####### ####### ####### ## ## ### ##
|
|
## ## ## ## ## ##
|
|
## ## ####### ## ## ### ##
|
|
## ## ## ## ## ## ## ##
|
|
####### ############ ###### ######
|
|
|
|
------- A MINIMAL PROCEDURAL LANGUAGE --------
|
|
|
|
|
|
PROGRAM
|
|
-------
|
|
|
|
A program is a set of declarations followed by a compound
|
|
statement. Here is the minimal T3X program:
|
|
|
|
DO END
|
|
|
|
|
|
COMMENTS
|
|
--------
|
|
|
|
A comment is started with an exclamation point (!) and extends
|
|
up to the end of the current line. Example:
|
|
|
|
DO END ! Do nothing
|
|
|
|
|
|
DECLARATIONS
|
|
------------
|
|
|
|
CONST name = cvalue, ... ;
|
|
|
|
Assign names to constant values.
|
|
|
|
Example: CONST false = 0, true = %1;
|
|
|
|
|
|
VAR name, ... ;
|
|
VAR name[cvalue], ... ;
|
|
VAR name::cvalue, ... ;
|
|
|
|
Define variables, vectors, and byte vectors, respectively.
|
|
Different definitions may be mixed. Vector elements start at
|
|
an index of 0.
|
|
|
|
Example: VAR stack[STACK_LEN], ptr;
|
|
|
|
|
|
STRUCT name = name_1, ..., name_N;
|
|
|
|
Shorthand for CONST name_1 = 0, ..., name_N = N-1, name = N;
|
|
Used to impose structure on vectors and byte vectors.
|
|
|
|
Example: STRUCT POINT = PX, PY, PCOLOR;
|
|
VAR p[POINT];
|
|
|
|
|
|
DECL name(cvalue), ... ;
|
|
|
|
Declare functions whose definitions follow later, where the
|
|
cvalue is the number of arguments. Used to implement mutual
|
|
recursion.
|
|
|
|
Example: DECL odd(1);
|
|
even(x) RETURN x=0-> 1: odd(x-1);
|
|
odd(x) RETURN x=1-> 1: even(x-1);
|
|
|
|
|
|
name(name_1, ...) statement
|
|
|
|
Define function "name" with arguments "name_1", ... and a
|
|
statement as its body. The number of arguments must match
|
|
any previous DECL of the same function.
|
|
|
|
The arguments of a function are only visible within the
|
|
(statement) of the function.
|
|
|
|
Example: hello(s, x) DO VAR i;
|
|
FOR (i=0, x) DO
|
|
writes(s);
|
|
writes("\n");
|
|
END
|
|
END
|
|
|
|
(Writes() writes a string; it is defined later in this text.)
|
|
|
|
|
|
STATEMENTS
|
|
----------
|
|
|
|
name := expression;
|
|
|
|
Assign the value of an expression to a variable.
|
|
|
|
Example: DO VAR x; x := 123; END
|
|
|
|
|
|
name[value]... := value;
|
|
name::value := value;
|
|
|
|
Assign the value of an expression to an element of a vector
|
|
or a byte vector. Multiple subscripts may be applied to to a
|
|
vector:
|
|
|
|
vec[i][j]... := i*j;
|
|
|
|
In general, VEC[i][j] denotes the j'th element of the i'th
|
|
element of VEC.
|
|
|
|
Note that the :: operator is right-associative, so v::x::i
|
|
equals v::(x::i). This is particularly important when mixing
|
|
subscripts, because
|
|
|
|
vec[i]::j[k] := 0;
|
|
|
|
would assign 0 to the j[k]'th element of vec[i]. (This makes
|
|
sense, because vec[i]::j would not deliver a valid address.)
|
|
|
|
|
|
name();
|
|
name(expression_1, ...);
|
|
|
|
Call the function with the given name, passing the values of the
|
|
expressions to the function. An empty set of parentheses is used
|
|
to pass zero arguments. The result of the function is discarded.
|
|
|
|
For further details see the description of function calls in the
|
|
expression section.
|
|
|
|
|
|
IF (condition) statement_1
|
|
IE (condition) statement_1 ELSE statement_2
|
|
|
|
Both of these statements run statement_1, if the given
|
|
condition is true.
|
|
|
|
In addition, IE/ELSE runs statement_2, if the conditions is
|
|
false. In this case, IF just passes control to the subsequent
|
|
statement.
|
|
|
|
Example: IE (0)
|
|
IF (1) RETURN 1;
|
|
ELSE
|
|
RETURN 2;
|
|
|
|
The example always returns 2, because only an IE statement can
|
|
have an ELSE branch. There is no "dangling else" problem.
|
|
|
|
|
|
WHILE (condition) statement
|
|
|
|
Repeat the statement while the condition is true. When the
|
|
condition is not true initially, never run the statement.
|
|
|
|
Example: ! Count from 1 to 10
|
|
DO VAR i;
|
|
i := 0;
|
|
WHILE (i < 10)
|
|
i := i+1;
|
|
END
|
|
|
|
|
|
FOR (name=expression_1, expression_2, cvalue) statement
|
|
FOR (name=expression_1, expression_2) statement
|
|
|
|
Assign the value of expression_1 to name, then compare name to
|
|
expression_2. If cvalue is not negative, repeat the statement
|
|
while name < expression_2. Otherwise repeat the statement while
|
|
name > expression_2. After running the statement, add cvalue
|
|
to name. Formally:
|
|
|
|
name := expression_1;
|
|
WHILE ( cvalue > 0 /\ name < expression \/
|
|
cvalue < 0 /\ name > expression )
|
|
DO
|
|
statement;
|
|
name := name + cvalue;
|
|
END
|
|
|
|
When the cvalue is omitted, it defaults to 1.
|
|
|
|
Examples: DO VAR i;
|
|
FOR (i=1, 11); ! count from 1 to 10
|
|
FOR (i=10, 0, %1); ! count from 10 to 1
|
|
END
|
|
|
|
|
|
LEAVE;
|
|
|
|
Leave the innermost WHILE or FOR loop, passing control to the
|
|
first statement following the loop.
|
|
|
|
Example: DO VAR i; ! Count from 1 to 50
|
|
FOR (i=1, 100) IF (i=50) LEAVE;
|
|
END
|
|
|
|
|
|
LOOP;
|
|
|
|
Re-enter the innermost WHILE or FOR loop. WHILE loops are
|
|
re-entered at the point where the condition is tested, and
|
|
FOR loops are re-entered at the point where the counter is
|
|
incremented.
|
|
|
|
Example: DO VAR i; ! This program never prints X
|
|
FOR (i=1, 10) DO
|
|
LOOP;
|
|
T.WRITE(1, "x", 1);
|
|
END
|
|
END
|
|
|
|
|
|
RETURN expression;
|
|
|
|
Return a value from a function. For further details see the
|
|
description of function calls in the expression section.
|
|
|
|
Example: inc(x) RETURN x+1;
|
|
|
|
|
|
HALT cvalue;
|
|
|
|
Halt program and return the given exit code to the operating
|
|
system.
|
|
|
|
Example: HALT 1;
|
|
|
|
|
|
DO statement ... END
|
|
DO declaration ... statement ... END
|
|
|
|
Compound statement of the form DO ... END are used to place
|
|
multiple statements in a context where only a single statement
|
|
is expected, like selection, loop, and function bodies.
|
|
|
|
A compound statement may declare its own local variables,
|
|
constant, and structures (using VAR, CONST, or STRUCT). A
|
|
local variable of a compound statement is created and
|
|
allocated at the beginning of the statement is ceases to
|
|
exist at the end of the statement.
|
|
|
|
Note that the form
|
|
|
|
DO declaration ... END
|
|
|
|
also exists, but is essentially an empty statement.
|
|
|
|
Example: DO var i, x; ! Compute 10 factorial
|
|
x := 1;
|
|
for (i=1, 10)
|
|
x := x*i;
|
|
END
|
|
|
|
|
|
DO END
|
|
;
|
|
|
|
These are both empty statements or null statements. They do not
|
|
do anything when run and may be used as placeholders where a
|
|
statement would be expected. They are also used to show that
|
|
nothing is to be done in a specific situation, like in
|
|
|
|
IE (x = 0)
|
|
;
|
|
ELSE IE (x < 0)
|
|
statement;
|
|
ELSE
|
|
statement;
|
|
|
|
Examples: FOR (i=0, 100000) DO END ! waste some time
|
|
|
|
|
|
EXPRESSIONS
|
|
-----------
|
|
|
|
An expression is a variable or a literal or a function call or
|
|
a set of operators applied to one of these. There are unary,
|
|
binary, and ternary operators.
|
|
|
|
Examples: -a ! negate a
|
|
b*c ! product of b and c
|
|
x->y:z ! if x then y else z
|
|
|
|
In the following, the symbols X, Y, and Z denote variables or
|
|
literals.
|
|
|
|
These operators exist (P denotes precedence, A associativity):
|
|
|
|
OPERATOR P A DESCRIPTION
|
|
|
|
X[Y] 9 L the Y'th element of the vector X
|
|
X::Y 9 R the Y'th byte of the byte vector X
|
|
|
|
-X 8 - the negative value of X
|
|
~X 8 - the bitwise inverse of X
|
|
\X 8 - logical NOT of X
|
|
@X 8 - the address of X
|
|
|
|
X*Y 7 L the product of X and Y
|
|
Y/Y 7 L the integer quotient of X and Y
|
|
X mod Y 7 L the division remainder of X and Y
|
|
|
|
X+Y 6 L the sum of X and Y
|
|
X-Y 6 L the difference between X and Y
|
|
|
|
X&Y 5 L the bitwise AND of X and Y
|
|
X|Y 5 L the bitwise OR of X and Y
|
|
X^Y 5 L the bitwise XOR of X and Y
|
|
X<<Y 5 L X shifted to the left by Y bits
|
|
X>>Y 5 L X shifted to the right by Y bits
|
|
|
|
X<Y 4 L %1, if X is less than Y, else 0
|
|
X>Y 4 L %1, if X is less than Y, else 0
|
|
X<=Y 4 L %1, if X is less/equal Y, else 0
|
|
X>=Y 4 L %1, if X is greater/equal Y, else 0
|
|
|
|
X=Y 3 L %1, if X equals Y, else 0
|
|
X\=Y 3 L %1, if X does not equal Y, else 0
|
|
|
|
X/\Y 2 L if X then Y else 0
|
|
(short-circuit logical AND)
|
|
|
|
X\/Y 1 L if X then X else Y
|
|
(short-circuit logical OR)
|
|
|
|
X->Y:Z 0 - if X then Y else Z
|
|
|
|
Higher precedence means that an operator binds stronger, e.g.
|
|
-X::Y actually means -(X::Y).
|
|
|
|
Left-associativity (L) means that x+y+z = (x+y)+z and
|
|
right-associativity (R) means that x::y::z = x::(y::z).
|
|
|
|
|
|
CONDITIONS
|
|
----------
|
|
|
|
A condition is an expression appearing in a condition context,
|
|
like the condition of an IF or WHILE statement or the first
|
|
operand of the X->Y:Z operator.
|
|
|
|
In an expression context, the value 0 is considered to be
|
|
"false", and any other value is considered to be true. For
|
|
example:
|
|
|
|
X=X is true
|
|
1=2 is false
|
|
"x" is true
|
|
5>7 is false
|
|
|
|
The canonical truth value, as returned by 1=1, is %1.
|
|
|
|
|
|
FUNCTION CALLS
|
|
--------------
|
|
|
|
When a function call appears in an expression, the result of
|
|
the function, as returned by RETURN is used as an operand.
|
|
|
|
A function call is performed as follows:
|
|
|
|
Each actual argument in the call
|
|
|
|
function(argument_1, ...)
|
|
|
|
is passed to the function and bound to the corresponding formal
|
|
argument ("argument") of the receiving function. The function
|
|
then runs its statement, which may produce a value via RETURN.
|
|
When no RETURN statement exists in the statement, 0 is returned.
|
|
|
|
Function arguments evaluate from the left to the right, so in
|
|
|
|
f(a,b,c);
|
|
|
|
A is guaranteed to evaluate before B and C and B is guaranteed
|
|
to evaluate before C.
|
|
|
|
Example: pow(x, y) DO VAR a;
|
|
a := 1;
|
|
WHILE (y) DO
|
|
a := a*x;
|
|
y := y-1;
|
|
END
|
|
RETURN a;
|
|
END
|
|
|
|
DO VAR x;
|
|
x := pow(2,10);
|
|
END
|
|
|
|
|
|
LITERALS
|
|
--------
|
|
|
|
INTEGERS
|
|
|
|
An integer is a number representing its own value. Note that
|
|
negative numbers have a leading '%' sign rather than a '-' sign.
|
|
While the latter also works, it is, strictly speaking, the
|
|
application of the '-' operator to a positive number, so it may
|
|
not appear in cvalue contexts.
|
|
|
|
Examples: 0
|
|
12345
|
|
%1
|
|
|
|
|
|
CHARACTERS
|
|
|
|
Characters are integers internally. They are represented by
|
|
single characters enclosed in single quotes. In addition, the
|
|
same escape sequences as in strings may be used.
|
|
|
|
Examples: 'x'
|
|
'\\'
|
|
'''
|
|
'\e'
|
|
|
|
|
|
STRINGS
|
|
|
|
A string is a byte vector filled with characters. Strings are
|
|
delimited by '"' characters and NUL-terminated internally. All
|
|
characters between the delimiting double quotes represent
|
|
themselves. In addition, the following escape sequences may be
|
|
used to include some special characters:
|
|
|
|
\a BEL Bell
|
|
\b BS Backspace
|
|
\e ESC Escape
|
|
\f FF Form Feed
|
|
\n LF Line Feed (newline)
|
|
\q " Quote
|
|
\r CR Carriage Return
|
|
\s Space
|
|
\t HT Horizontal Tabulator
|
|
\v VT Vertical Tabulator
|
|
\\ \ Backslash
|
|
|
|
Examples: ""
|
|
"hello, world!\n"
|
|
"\qhi!\q, she said"
|
|
|
|
|
|
TABLES
|
|
|
|
A table is a vector literal, i.e. a sequence of subsequent
|
|
values. It is delimited by square brackets and elements are
|
|
separated by commas. Table elements can be cvalues, strings,
|
|
and tables.
|
|
|
|
Examples: [1, 2, 3]
|
|
["5 times -7", %35]
|
|
[[1,0,0],[0,1,0],[0,0.1]]
|
|
|
|
|
|
DYNAMIC TABLES
|
|
|
|
The dynamic table is a special case of the table in which one
|
|
or multiple elements are computed at program run time. Dynamic
|
|
table elements are enclosed in parentheses. E.g. in the table
|
|
|
|
["x times 7", (x*7)]
|
|
|
|
the value of the second element would be computed and filled
|
|
in when the table is being evaluated. Note that dynamic table
|
|
elements are being replaced in situ, and remain the same only
|
|
until they are replaced again.
|
|
|
|
Multiple dynamic elements may be enclosed by a single pair of
|
|
parentheses. For instance, the following tables are the same:
|
|
|
|
[(x), (y), (z)]
|
|
[(x, y, z)]
|
|
|
|
|
|
CVALUES
|
|
-------
|
|
|
|
A cvalue (constant value) is an expression whose value is known
|
|
at compile time. In full T3X, this is a large subset of full
|
|
expressions, but in T3X9, it it limited to the following:
|
|
|
|
* integers
|
|
* characters
|
|
* constants
|
|
|
|
as well as (given that X and Y are one of the above):
|
|
|
|
* X+Y
|
|
* X*Y
|
|
|
|
|
|
NAMING CONVENTIONS
|
|
------------------
|
|
|
|
Symbolic names for variables, constants, structures, and
|
|
functions are constructed from the following alphabet:
|
|
|
|
* the characters a-z
|
|
* the digits 0-9
|
|
* the special characters '_' and '.'
|
|
|
|
The first character of a name must be non-numeric, the remaining
|
|
characters may be any of the above.
|
|
|
|
Upper and lower case is not distinguished, the symbolic names
|
|
|
|
FOO, Foo, foo
|
|
|
|
are all considered to be equal.
|
|
|
|
By convention,
|
|
|
|
* CONST names are all upper-case
|
|
* STRUCT names are all upper-case
|
|
* global VAR names are capitalized
|
|
* local VAR names are all lower-case
|
|
* function names are all lower-case
|
|
|
|
Keywords, like VAR, IF, DO, etc, are sometimes printed in upper
|
|
case in documentation, but are usually in lower case in actual
|
|
programs.
|
|
|
|
|
|
SHADOWING
|
|
---------
|
|
|
|
There is a single name space without any shadowing in T3X:
|
|
|
|
* all global names must be different
|
|
* no local name may have the same name as a global name
|
|
* all local names in the same scope must be different
|
|
|
|
The latter means that local names may be re-used in subsequent
|
|
scopes, e.g.:
|
|
|
|
f(x) RETURN x;
|
|
g(x) RETURN x;
|
|
|
|
would be a valid program. However,
|
|
|
|
f(x) DO VAR x; END !!! WRONG !!!
|
|
|
|
would not be a valid program, because VAR x; redefines the
|
|
argument of F.
|
|
|
|
|
|
BUILT-IN FUNCTIONS
|
|
------------------
|
|
|
|
The following built-in functions exist in T3X9. They resemble
|
|
the functions of the T3X core module of the full language, i.e.
|
|
a T3X9 program can be compiled by a T3X compiler by adding the
|
|
following code to the top of the program:
|
|
|
|
MODULE name(t3x);
|
|
OBJECT t[t3x].
|
|
|
|
These functions are built into the T3X9 compiler, though, and
|
|
do not have to be declared in any way. The '.' in the function
|
|
names resembles the message operator of the full language.
|
|
|
|
|
|
T.READ(fd, buf, len)
|
|
|
|
Read up to LEN characters from the file descriptor FD into the
|
|
buffer BUF. Return the number of characters actually read.
|
|
Return %1 in case of an error.
|
|
|
|
|
|
T.WRITE(fd, buf, len)
|
|
|
|
Write LEN characters from the buffer BUF to the file descriptor
|
|
FD. Return the number of characters actually written. Return %1
|
|
in case of an error.
|
|
|
|
Example: t.write(1, "hello, world!\n", 14);
|
|
|
|
|
|
T.MEMCOMP(b1, b2, len)
|
|
|
|
Compare the first LEN bytes of the byte vectors B1 and B2.
|
|
Return the difference of the first pair of mismatching bytes.
|
|
A return code of 0 means that the compared regions are equal.
|
|
|
|
Example: t.memcomp("aaa", "aba", 3) ! gives 'b'-'a' = %1
|
|
|
|
|
|
T.MEMCOPY(bs, bd, len)
|
|
|
|
Copy LEN bytes from the byte vector BS (source) to the byte
|
|
vector BD (destination). Return 0.
|
|
|
|
Example: DO VAR b:100; t.memcopy("hello", b, 5); END
|
|
|
|
|
|
T.MEMFILL(bv, b, len)
|
|
|
|
Fill the first LEN bytes of the byte vector BV with the byte
|
|
value B. Return 0.
|
|
|
|
Example: DO VAR b:100; t.memfill(b, 0, 100); END
|
|
|
|
|
|
T.MEMSCAN(bv, b, len)
|
|
|
|
Locate the first occurrence of the byte value B in the first LEN
|
|
bytes of the byte vector BV and return its offset in the vector.
|
|
When B does not exist in the given region, return %1.
|
|
|
|
Example: t.memscan("aaab", 'b', 4) ! returns 3
|
|
|
|
|
|
VARIADIC FUNCTIONS
|
|
------------------
|
|
|
|
T3X implements variadic functions (i.e. functions of a variable
|
|
number of arguments) using dynamic tables. For instance, the
|
|
following function returns the sum of a vector of arguments:
|
|
|
|
sum(k, v) DO var i, n;
|
|
n := 0;
|
|
FOR (i=0, k)
|
|
n := n+v[i];
|
|
RETURN n;
|
|
END
|
|
|
|
Its is an ordinary function returning the sum of a vector. It
|
|
can be considered to be a variadic function, because a dynamic
|
|
table can be passed to it in the V argument:
|
|
|
|
sum(5, [(a,b,c,d,e)])
|
|
|
|
|
|
EXAMPLE PROGRAM
|
|
---------------
|
|
|
|
var ntoa_buf::100;
|
|
|
|
ntoa(x) do var i, k;
|
|
if (x = 0) return "0";
|
|
i := 0;
|
|
k := x<0-> -x: x;
|
|
while (k > 0) do
|
|
i := i+1;
|
|
k := k/10;
|
|
end
|
|
i := i+1;
|
|
if (x < 0) i := i+1;
|
|
ntoa_buf::i := 0;
|
|
k := x<0-> -x: x;
|
|
while (k > 0) do
|
|
i := i-1;
|
|
ntoa_buf::i := '0' + k mod 10;
|
|
k := k/10;
|
|
end
|
|
if (x < 0) do
|
|
i := i-1;
|
|
ntoa_buf::i := '-';
|
|
end
|
|
return @ntoa_buf::i;
|
|
end
|
|
|
|
str.length(s) return t.memscan(s, 0, 32767);
|
|
|
|
writes(s) t.write(1, s, str.length(s));
|
|
|
|
fib(n) do var r1, r2, i, t;
|
|
r1 := 0;
|
|
r2 := 1;
|
|
for (i=1, n) do
|
|
t := r2;
|
|
r2 := r2 + r1;
|
|
r1 := t;
|
|
end
|
|
return r2;
|
|
end
|
|
|
|
do var i;
|
|
for (i=1, 11) do
|
|
writes(ntoa(fib(i)));
|
|
writes("\n");
|
|
end
|
|
end
|