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 = N1, 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(x1);


odd(x) RETURN x=1> 1: even(x1);






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 rightassociative, 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;




Reenter the innermost WHILE or FOR loop. WHILE loops are


reentered at the point where the condition is tested, and


FOR loops are reentered 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


XY 6 L the difference between X and Y




X&Y 5 L the bitwise AND of X and Y


XY 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


(shortcircuit logical AND)




X\/Y 1 L if X then X else Y


(shortcircuit 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).




Leftassociativity (L) means that x+y+z = (x+y)+z and


rightassociativity (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 := y1;


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 NULterminated 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 az


* the digits 09


* the special characters '_' and '.'




The first character of a name must be nonnumeric, 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 uppercase


* STRUCT names are all uppercase


* global VAR names are capitalized


* local VAR names are all lowercase


* function names are all lowercase




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 reused 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.






BUILTIN FUNCTIONS







The following builtin 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 := i1;


ntoa_buf::i := '0' + k mod 10;


k := k/10;


end


if (x < 0) do


i := i1;


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
