trunk
Alona EM 2022-05-12 14:47:58 +01:00
commit e840718997
32 changed files with 11087 additions and 0 deletions

23
t3x9-book/Makefile Normal file
View File

@ -0,0 +1,23 @@
all: t0 t.elf
t0: t.c t.t
cc -static -o t0 t.c
t.elf: test
cp t3 t.elf
test: t0
touch t1 t2 t3; chmod +x t1 t2 t3
./t0 <t.t >t1 && ./t1 <t.t >t2 && ./t2 <t.t >t3 && cmp t2 t3
mksums: clean
ls | grep -v t3x9.tgz | grep -v _csums | csum -m >_csums
csums:
csum -u <_csums >_csums.new && mv -f _csums.new _csums
clean:
rm -f t0 t1 t2 t3 a.out dump *.o *.core t3x9.tgz
arc: clean
(cd ..; tar cvf - t3x9 | gzip -9 >t3x9.tgz); mv -f ../t3x9.tgz .

20
t3x9-book/README Normal file
View File

@ -0,0 +1,20 @@
This is the T3X9 compiler, as described in the book
"Write Your Own Compiler"
by Nils M Holm
More details about the book can be found at T3X.ORG.
To compile the compiler on FreeBSD, just do
chmod +x t.elf && ./t.elf <t.t >t.new
To compile it on any system providing a C89 compiler:
cc -o t0 t.c && ./t0 <t.t >t.new
NOTE: some patches have been applied to the compiler since
publication. The source code in the "t.t" file reflects the
latest patchlevel. Patches are contained in the files t.pl?.

14
t3x9-book/_csums Normal file
View File

@ -0,0 +1,14 @@
41114 1 Makefile
50469 1 README
14051 1 dump.c
15789 26 t.c
55434 31 t.elf
16731 1 t.pl1
41318 1 t.pl2
55592 1 t.pl3
47836 1 t.pl4
9277 28 t.t
39214 3 t3x-history.txt
45560 3 t3x.bnf
58677 17 t3x.txt
50723 1 test.t

21
t3x9-book/dump.c Normal file
View File

@ -0,0 +1,21 @@
#include <stdio.h>
#define Z 65536
#define byte unsigned char
byte T[Z];
int main(void) {
int k, i;
k = fread(T, 1, Z, stdin);
printf(".byte ");
for (i=0x74; i<k; i++) {
if (i % 10 == 0)
printf("\n.byte ");
printf("0x%02x", T[i]);
if ((i+1) % 10 != 0 && i+1 < k)
printf(",");
}
printf("\n");
}

1562
t3x9-book/t.c Normal file

File diff suppressed because it is too large Load Diff

BIN
t3x9-book/t.elf Executable file

Binary file not shown.

30
t3x9-book/t.pl1 Normal file
View File

@ -0,0 +1,30 @@
--- t.t.old 2017-05-18 10:13:35.000000000 +0200
+++ t.t 2017-05-18 10:14:16.000000000 +0200
@@ -1217,7 +1217,7 @@
gen(CG_JMPFALSE, 0);
xrparen();
stmt();
- ie (alt) do
+ if (alt) do
gen(CG_JUMPFWD, 0);
swap();
gen(CG_RESOLV, 0);
@@ -1225,9 +1225,6 @@
T := scan();
stmt();
end
- else if (T = KELSE) do
- aw("ELSE without IE", 0);
- end
gen(CG_RESOLV, 0);
end
@@ -1370,6 +1367,8 @@
if_stmt(1);
else ie (T = KIF)
if_stmt(0);
+ else ie (T = KELSE)
+ aw("ELSE without IE", 0);
else ie (T = KLEAVE)
leave_stmt();
else ie (T = KLOOP)

11
t3x9-book/t.pl2 Normal file
View File

@ -0,0 +1,11 @@
--- t.t.old 2017-05-23 10:43:30.000000000 +0200
+++ t.t 2017-05-23 10:43:50.000000000 +0200
@@ -906,6 +906,8 @@
fncall(fn) do var i;
T := scan();
if (fn = 0) aw("call of non-function", 0);
+ if (fn[SFLAGS] & (FUNC|FORW) = 0)
+ aw("call of non-function", fn[SNAME]);
i := 0;
while (T \= RPAREN) do
expr(0);

23
t3x9-book/t.pl3 Normal file
View File

@ -0,0 +1,23 @@
--- t.t.old 2018-03-22 08:59:31.000000000 +0100
+++ t.t 2018-03-22 09:01:22.000000000 +0100
@@ -1389,15 +1389,18 @@
else
expect(%1, "statement");
-compound() do var oyp, olp, onp;
+compound() do var oyp, olp, onp, msg;
+ msg := "unexpected end of compound statement";
T := scan();
oyp := Yp;
onp := Np;
olp := Lp;
while (T = KVAR \/ T = KCONST \/ T = KSTRUCT)
declaration(0);
- while (T \= KEND)
+ while (T \= KEND) do
+ if (T = ENDFILE) aw(msg, 0);
stmt();
+ end
T := scan();
if (olp-Lp \= 0)
gen(CG_DEALLOC, olp-Lp);

23
t3x9-book/t.pl4 Normal file
View File

@ -0,0 +1,23 @@
--- t.t.old 2019-07-20 12:04:34.000000000 +0200
+++ t.t 2019-07-20 12:03:27.000000000 +0200
@@ -1510,9 +1510,9 @@
tfill := "8b7c240c8b4424088b4c2404fcf3aa31c0c3";
tscan :=
"8b7c240c8b4424088b4c24044189fafcf2ae09c90f840600000089f829d048c331c048c3";
- Ops := [[ 7, 1, "mod", BINOP, CG_MOD ],
+ Ops := [[ 7, 3, "mod", BINOP, CG_MOD ],
[ 6, 1, "+", BINOP, CG_ADD ],
- [ 7, 2, "*", BINOP, CG_MUL ],
+ [ 7, 1, "*", BINOP, CG_MUL ],
[ 0, 1, ";", SEMI, 0 ],
[ 0, 1, ",", COMMA, 0 ],
[ 0, 1, "(", LPAREN, 0 ],
@@ -1539,7 +1539,7 @@
[ 5, 2, ">>", BINOP, CG_SHR ],
[ 6, 1, "-", BINOP, CG_SUB ],
[ 0, 2, "->", COND, 0 ],
- [ 7, 2, "/", BINOP, CG_DIV ],
+ [ 7, 1, "/", BINOP, CG_DIV ],
[ 2, 2, "/\\", CONJ, 0 ],
[ 0, 0, 0, 0, 0 ] ];
Equal_op := findop("=");

1574
t3x9-book/t.t Normal file

File diff suppressed because it is too large Load Diff

59
t3x9-book/t3x-history.txt Normal file
View File

@ -0,0 +1,59 @@
A SHORT HISTORY OF THE T3X LANGUAGE
T3X is a tiny block-structured language that you probably
haven't heard about. It had a tiny community back in the
mid-1990's. Software written in T3X includes its own compiler
(of course), its own text-based IDE, a few LISP interpreters,
an assembler and linker for the 8086, and a database system
used by a local church community. It was also used in a few
college courses, most probably because its community was so
tiny that nobody could be bothered to do your homework
assignments for you.
The T3X language started as a very minimalistic language
with a single-file compiler that targeted the 8086 and 386
processors. It supported FreeBSD via the GNU binutils and
emitted DOS EXE files through its own assembler and linker.
In its lifetime, several enhancements were made to both the
language and its implementation:
- An object system was added to the language, and the entire
runtime support infrastructure was rewritten as a set of
classes.
- Tcode, an abstract target language, was added. It could be
interpreted, optimized, linked, and converted to native
code.
- A back-end for the AXP 21064 (Alpha) was added.
- A C back-end was added, allowing to use T3X on otherwise
unsupported processors.
- Runtime support for the following platforms was added:
NetBSD-386, NetBSD-Alpha, FreeBSD-386, Coherent-386,
Linux-386, and Plan 9 (via C).
T3X is probably notable, because it is a typeless object
oriented language. Objects are distinguished by the methods
they implement, and the methods are typeless procedures.
The T3X object system is more similar to ADA packages than
to the C++ or Java approach. It implements reusable modules
rather than data types.
T3X-8.1.7 was the last version of T3X and it was released in
2004 with some minor updates in 2011 and 2014. Its generic
(Tcode) port still runs on modern operating systems.
T3X9 is a subset of the T3X language that compiles directly
from T3X to ELF-FreeBSD-386.
If you are familiar with T3X, this is what the compiler omits
from the original language: modules, objects, classes, packed
vectors, function pointers and indirect function calls, meta
commands, unsigned operators. Also, constant expression syntax
is only a subset.
The T3X9 compiler is under 1600 lines in size and compiles
itself from source to ELF in about 0.06 seconds on a 750MHz
notebook computer. The resulting binary has a size of less
than 32K bytes.

221
t3x9-book/t3x.bnf Normal file
View File

@ -0,0 +1,221 @@
%token SYMBOL, INTEGER, CHARACTER, STRING
%token VAR, CONST, STRUCT, DECL, DO, END
%token IF, IE, ELSE, WHILE, FOR, LEAVE, LOOP,
%token RETURN, HALT, MODULO
%%
Program:
Declarations CompoundStmt
;
Declarations:
Declaration
| Declaration Declarations
;
Declaration:
VAR VarList ';'
| CONST ConstList ';'
| DECL DeclList ';'
| STRUCT SYMBOL '=' StructMembers ';'
| FunctionDecl
;
VarList:
SYMBOL
| SYMBOL '[' ConstValue ']'
| VarList ',' SYMBOL
;
ConstList:
ConstDef
| ConstDef ',' ConstList
;
ConstDef:
SYMBOL '=' ConstValue
;
DeclList:
Decl
| Decl ',' DeclList
;
Decl:
SYMBOL '(' ConstValue ')'
;
StructMembers:
SYMBOL
| SYMBOL ',' StructMembers ';'
;
FunctionDecl:
SYMBOL '(' OptFormalArgs ')' Statement
;
OptFormalArgs:
| ArgumentList
;
ArgumentList:
SYMBOL
| SYMBOL ',' ArgumentList
;
Statement:
CompoundStmt
| SYMBOL ':=' Expression ';'
| SYMBOL Subscripts ':=' Expression ';'
| FunctionCall
| IF '(' Expression ')' Statement
| IE '(' Expression ')' Statement
ELSE Statement
| WHILE '(' Expression ')' Statement
| FOR '(' SYMBOL '=' Expression ','
Expression ')'
Statement
| FOR '(' SYMBOL '=' Expression ','
Expression,
ConstValue ')'
Statement
| LEAVE ';'
| LOOP ';'
| RETURN Expression ';'
| HALT ConstValue ';'
| ';'
;
CompoundStmt:
DO END
| DO LocalDecls END
| DO StatementList END
| DO LocalDecls StatementList END
;
LocalDecls:
LocalDecl
| LocalDecl LocalDecls
;
LocalDecl:
VAR VarList ';'
| CONST ConstList ';'
| STRUCT SYMBOL '=' StructMembers ';'
;
StatementList:
Statement
| Statement StatementList
;
ExprList:
Expression
| Expression ',' ExprList
;
Expression:
Disjunction
| Disjunction '->' Expression ':' Expression
;
Disjunction:
Conjunction
| Conjunction '/\\' Disjunction
;
Conjunction:
Equation
| Equation '\\/' Conjunction
;
Equation:
Relation
| Relation '=' Equation
| Relation '\\=' Equation
;
Relation:
BitOperation
| BitOperation '<' Relation
| BitOperation '>' Relation
| BitOperation '<=' Relation
| BitOperation '>=' Relation
;
BitOperation:
Sum
| Sum '&' BitOperation
| Sum '|' BitOperation
| Sum '^' BitOperation
| Sum '<<' BitOperation
| Sum '>>' BitOperation
;
Sum:
Term
| Term '+' Sum
| Term '-' Sum
;
Term:
Factor
| Factor '*' Term
| Factor '/' Term
| Factor MODULO Term
;
Factor:
INTEGER
| FunctionCall
| STRING
| Table
| SYMBOL
| SYMBOL Subscripts
| '@' SYMBOL
| '@' SYMBOL Subscripts
| '-' Factor
| '\\' Factor
| '~' Factor
| '(' Expression ')'
;
Subscripts:
'[' Expression ']'
| '[' Expression ']' Subscripts
| '::' Factor
;
Table:
'[' MemberList ']'
;
MemberList:
TableMember
| TableMember ',' MemberList
;
TableMember:
ConstValue
| STRING
| Table
| '(' ExprList ')'
;
FunctionCall:
SYMBOL '(' ')'
| SYMBOL '(' ExprList ')'
;
ConstValue:
SYMBOL
| Integer
;
Integer:
INTEGER
| CHARACTER
;
%%

689
t3x9-book/t3x.txt Normal file
View File

@ -0,0 +1,689 @@
################# ############ ###### ######
## ## ## ## ## ## ## ##
####### ####### ####### ## ## ### ##
## ## ## ## ## ##
## ## ####### ## ## ### ##
## ## ## ## ## ## ## ##
####### ############ ###### ######
------- 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

47
t3x9-book/test.t Normal file
View File

@ -0,0 +1,47 @@
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

29
t3x9r3-extended/CHANGES Normal file
View File

@ -0,0 +1,29 @@
************************************************************************
IMPORTANT:
The book version of the T3X compiler uses a T.MEMCOPY procedure with
the following arguments:
T.MEMCOPY(SOURCE, DESTINATION, LENGTH)
Note that this version is incompatible with the original T3X language,
which uses
T.MEMCOPY(DESTINATION, SOURCE, LENGTH)
The compiler in this archive uses the original T3X version with the
destination argument to the left of the source argument. Hence it is
slightly incompatible to the book version!
************************************************************************
20200304 Added MODULE and OBJECT dummy declarations (T3X compatibility).
20190913 Made T.MEMCOPY compatible with the original T3X version.
20190720 Fixed harmless minor inconsistency in operator table.
20170605 Add hexa-decimal integer literals to language.
20170605 Add T3X packed vectors (byte vectors) to language.
20170528 Add tcdis, Tcode9 disassembler.
20170528 Add tcvm, ad-hoc Tcode9 virtual machine.
20170523 Add t-vm.t, T3X9->Tcode9 compiler.
20170520 Fix: DO VAR x; x(); END would not report call of non-function.
20170518 Fix parser bug mis-parsing IE (x) IF (y) v; ELSE w;.
20170430 Add T.CREATE, T.OPEN, T.CLOSE, T.RENAME, T.REMOVE built-ins.

17
t3x9r3-extended/MANIFEST Normal file
View File

@ -0,0 +1,17 @@
CHANGES change log
MANIFEST this file
Makefile Makefile
README read me
_csums checksums
dump.c dump code part of a T3X9-generated ELF file
t-vm.t T3X9->Tcode9 compiler
t.c T3X9->ELF boostrapping compiler
t.elf T3X9->ELF compiler, FreeBSD-386-ELF executable
t.t T3X9->ELF compiler
t.vm T3X9->Tcode9 compiler, Tcode9 VM executable
t3x-history.txt short summary of T3X language
t3x.bnf yaccable T3X9 grammar
t3x.txt T3X9 micro manual
tcdis.c Tcode9 disassembler
tcvm.c ad-hoc Tcode9 virtual machine
test.t test program

42
t3x9r3-extended/Makefile Normal file
View File

@ -0,0 +1,42 @@
D= t3x9r3
A= $D.tgz
all: t0 t.elf t.vm tcvm
t0: t.c t.t
cc -static -o t0 t.c
t.vm: t-vm
./t-vm <t-vm.t >t.vm
t-vm: t-vm.t
./t3 <t-vm.t >t-vm && chmod +x t-vm
tcvm: tcvm.c
cc -O2 -g -o tcvm tcvm.c
t.elf: test
cp t3 t.elf
test: t0
touch t1 t2 t3; chmod +x t1 t2 t3
./t0 <t.t >t1 && ./t1 <t.t >t2 && ./t2 <t.t >t3 && cmp t2 t3
vmtest: tcvm
./tcvm t.vm <t-vm.t >t1.vm
./tcvm t1.vm <t-vm.t >t2.vm
./tcvm t2.vm <t-vm.t >t3.vm
cmp t2.vm t3.vm
mksums: clean
ls | grep -v $A | grep -v _csums | csum -m >_csums
csums:
csum -u <_csums >_csums.new && mv -f _csums.new _csums
clean:
rm -f t0 t1 t2 t3 t1.vm t2.vm t3.vm t-vm a.out tcvm dump *.o *.core \
$A
arc: clean
(cd ..; tar cvf - $D | gzip -9 >$A); mv -f ../$A .

30
t3x9r3-extended/README Normal file
View File

@ -0,0 +1,30 @@
This is the T3X9 compiler, Release 2
It is superset of the compiler described in
"Write Your Own Compiler"
by Nils M Holm
More details about the book can be found at T3X.ORG.
*********************************************************
Please consult the CHANGES file before usig this version!
*********************************************************
To compile the compiler on FreeBSD-386, just do
chmod +x t.elf && ./t.elf <t.t >t.new
To compile it on any system providing a C89 compiler:
cc -o t0 t.c && ./t0 <t.t >t.new
Alternatively, use the Tcode9 virtual machine:
cc -o tcvm tcvm.c && ./tcvm t.vm <t-vm.t >t.new
You can then compile T3X9 programs using
./tcvm t.new <t3x-program >vm-program

16
t3x9r3-extended/_csums Normal file
View File

@ -0,0 +1,16 @@
15731 2 CHANGES
46710 1 MANIFEST
13049 1 Makefile
62480 1 README
14051 1 dump.c
37533 29 t-vm.t
32747 27 t.c
10398 34 t.elf
26599 31 t.t
31885 19 t.vm
28851 3 t3x-history.txt
45560 3 t3x.bnf
32461 21 t3x.txt
33423 6 tcdis.c
7233 7 tcvm.c
33987 1 test.t

21
t3x9r3-extended/dump.c Normal file
View File

@ -0,0 +1,21 @@
#include <stdio.h>
#define Z 65536
#define byte unsigned char
byte T[Z];
int main(void) {
int k, i;
k = fread(T, 1, Z, stdin);
printf(".byte ");
for (i=0x74; i<k; i++) {
if (i % 10 == 0)
printf("\n.byte ");
printf("0x%02x", T[i]);
if ((i+1) % 10 != 0 && i+1 < k)
printf(",");
}
printf("\n");
}

1651
t3x9r3-extended/t-vm.t Normal file

File diff suppressed because it is too large Load Diff

1616
t3x9r3-extended/t.c Normal file

File diff suppressed because it is too large Load Diff

BIN
t3x9r3-extended/t.elf Normal file

Binary file not shown.

1682
t3x9r3-extended/t.t Normal file

File diff suppressed because it is too large Load Diff

BIN
t3x9r3-extended/t.vm Executable file

Binary file not shown.

View File

@ -0,0 +1,61 @@
A SHORT HISTORY OF THE T3X LANGUAGE
T3X is a tiny block-structured language that you probably
haven't heard about. It had a tiny community back in the
mid-1990's. Software written in T3X includes its own compiler
(of course), its own text-based IDE, a few LISP interpreters,
an assembler and linker for the 8086, and a database system
used by a local church community. It was also used in a few
college courses, most probably because its community was so
tiny that nobody could be bothered to do your homework
assignments for you.
The T3X language started as a very minimalistic language
with a single-file compiler that targeted the 8086 and 386
processors. It supported FreeBSD via the GNU binutils and
emitted DOS EXE files through its own assembler and linker.
In its lifetime, several enhancements were made to both the
language and its implementation:
- An object system was added to the language, and the entire
runtime support infrastructure was rewritten as a set of
classes.
- Tcode, an abstract target language, was added. It could be
interpreted, optimized, linked, and converted to native
code.
- A back-end for the AXP 21064 (Alpha) was added.
- A C back-end was added, allowing to use T3X on otherwise
unsupported processors.
- Runtime support for the following platforms was added:
NetBSD-386, NetBSD-Alpha, FreeBSD-386, Coherent-386,
Linux-386, and Plan 9 (via C).
T3X is probably notable, because it is a typeless object
oriented language. Objects are distinguished by the methods
they implement, and the methods are typeless procedures.
The T3X object system is more similar to ADA packages than
to the C++ or Java approach. It implements reusable modules
rather than data types.
T3X-8.1.7 was the last version of T3X and it was released in
2004 with some minor updates in 2011 and 2014. Its generic
(Tcode) port still runs on modern operating systems.
T3X9 is a subset of the T3X language that compiles directly
from T3X to ELF-FreeBSD-386.
If you are familiar with T3X, this is what the compiler omits
from the original language: modules, objects, classes, packed
vectors, function pointers and indirect function calls, meta
commands, unsigned operators. Also, constant expression syntax
is only a subset.
The T3X9 compiler is under 1600 lines in size and compiles
itself from source to ELF in about 0.06 seconds on a 750MHz
notebook computer. The resulting binary has a size of less
than 32K bytes. The T3X9r2 compiler has a slightly larger
size (about 33K bytes) and additional language constructs
(packed tables, hex literal prefixes) and runtime functions.

221
t3x9r3-extended/t3x.bnf Normal file
View File

@ -0,0 +1,221 @@
%token SYMBOL, INTEGER, CHARACTER, STRING
%token VAR, CONST, STRUCT, DECL, DO, END
%token IF, IE, ELSE, WHILE, FOR, LEAVE, LOOP,
%token RETURN, HALT, MODULO
%%
Program:
Declarations CompoundStmt
;
Declarations:
Declaration
| Declaration Declarations
;
Declaration:
VAR VarList ';'
| CONST ConstList ';'
| DECL DeclList ';'
| STRUCT SYMBOL '=' StructMembers ';'
| FunctionDecl
;
VarList:
SYMBOL
| SYMBOL '[' ConstValue ']'
| VarList ',' SYMBOL
;
ConstList:
ConstDef
| ConstDef ',' ConstList
;
ConstDef:
SYMBOL '=' ConstValue
;
DeclList:
Decl
| Decl ',' DeclList
;
Decl:
SYMBOL '(' ConstValue ')'
;
StructMembers:
SYMBOL
| SYMBOL ',' StructMembers ';'
;
FunctionDecl:
SYMBOL '(' OptFormalArgs ')' Statement
;
OptFormalArgs:
| ArgumentList
;
ArgumentList:
SYMBOL
| SYMBOL ',' ArgumentList
;
Statement:
CompoundStmt
| SYMBOL ':=' Expression ';'
| SYMBOL Subscripts ':=' Expression ';'
| FunctionCall
| IF '(' Expression ')' Statement
| IE '(' Expression ')' Statement
ELSE Statement
| WHILE '(' Expression ')' Statement
| FOR '(' SYMBOL '=' Expression ','
Expression ')'
Statement
| FOR '(' SYMBOL '=' Expression ','
Expression,
ConstValue ')'
Statement
| LEAVE ';'
| LOOP ';'
| RETURN Expression ';'
| HALT ConstValue ';'
| ';'
;
CompoundStmt:
DO END
| DO LocalDecls END
| DO StatementList END
| DO LocalDecls StatementList END
;
LocalDecls:
LocalDecl
| LocalDecl LocalDecls
;
LocalDecl:
VAR VarList ';'
| CONST ConstList ';'
| STRUCT SYMBOL '=' StructMembers ';'
;
StatementList:
Statement
| Statement StatementList
;
ExprList:
Expression
| Expression ',' ExprList
;
Expression:
Disjunction
| Disjunction '->' Expression ':' Expression
;
Disjunction:
Conjunction
| Conjunction '/\\' Disjunction
;
Conjunction:
Equation
| Equation '\\/' Conjunction
;
Equation:
Relation
| Relation '=' Equation
| Relation '\\=' Equation
;
Relation:
BitOperation
| BitOperation '<' Relation
| BitOperation '>' Relation
| BitOperation '<=' Relation
| BitOperation '>=' Relation
;
BitOperation:
Sum
| Sum '&' BitOperation
| Sum '|' BitOperation
| Sum '^' BitOperation
| Sum '<<' BitOperation
| Sum '>>' BitOperation
;
Sum:
Term
| Term '+' Sum
| Term '-' Sum
;
Term:
Factor
| Factor '*' Term
| Factor '/' Term
| Factor MODULO Term
;
Factor:
INTEGER
| FunctionCall
| STRING
| Table
| SYMBOL
| SYMBOL Subscripts
| '@' SYMBOL
| '@' SYMBOL Subscripts
| '-' Factor
| '\\' Factor
| '~' Factor
| '(' Expression ')'
;
Subscripts:
'[' Expression ']'
| '[' Expression ']' Subscripts
| '::' Factor
;
Table:
'[' MemberList ']'
;
MemberList:
TableMember
| TableMember ',' MemberList
;
TableMember:
ConstValue
| STRING
| Table
| '(' ExprList ')'
;
FunctionCall:
SYMBOL '(' ')'
| SYMBOL '(' ExprList ')'
;
ConstValue:
SYMBOL
| Integer
;
Integer:
INTEGER
| CHARACTER
;
%%

768
t3x9r3-extended/t3x.txt Normal file
View File

@ -0,0 +1,768 @@
################ ############ ###### ######
## ## ## ## ## ## ## ##
###### ###### ####### ## ## ### ##
## ## ## ## ## ##
## ## ####### ## ## ### ##
## ## ## ## ## ## ## ##
######## ############ ###### ######
----==[ 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.
Integers may have a '0x' prefix (after the '%' prefix, if
that also exists). In this case, the subsequent digits will
be interpreted as a hexa-decimal number.
Examples: 0
12345
%1
0xfff
%0xA5
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"
PACKED TABLES
A packed table is a byte vector literal. It is a set of cvalues
delimited by square brackets and separated by commas. Note that
string notation is a short and portable, but also limited,
notation for byte vectors. For instance, the byte vectors
"HELLO"
PACKED [ 'H', 'E', 'L', 'L', 'O', 0 ]
are identical. Byte vectors can contain any values in the range
from 0 to 255.
Examples: PACKED [ 1 ]
PACKED [ 1, 2, 3 ]
PACKED [ 14, 'H', 'i', 15 ]
TABLES
A table is a vector literal, i.e. a sequence of 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.
T3X9r3 accepts (and ignores) the above declarations at the
beginning of a program. A program containing these declarations
can be compiled by any T3X compiler.
MEMORY FUNCTIONS
-----[ 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.
Unlike in the full T3X language, BS and BD may not overlap.
Example: DO VAR b::100; t.memcopy(b, "hello", 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
INPUT/OUTPUT FUNCTIONS
-----[ T.CREATE(path) ]-----------------------------------------
Create a file with the given PATH, open it, and return its file
descriptor. In case of an error, return -1.
Example: t.create("new-file");
-----[ T.OPEN(path, mode) ]-------------------------------------
Open file PATH in the given MODE, where 0=read-only, 1=write-
only, and 2=read/write. Return -1 in case of an error.
Example: t.open("existing-file", 0);
-----[ T.CLOSE(fd) ]--------------------------------------------
Close the file descriptor FD. Return 0 for success and -1 in
case of an error.
Example: DO var fd;
fd := t.create("file");
if (fd >= 0) t.close();
END
-----[ 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.
Example: DO b::100; t.read(0, b, 99); END
-----[ 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.RENAME(path, new) ]------------------------------------
Rename the file given in PATH to NEW. Return 0 for success and
-1 in case of an error.
Example: t.rename("old-name", "new-name");
-----[ T.REMOVE(path) ]-----------------------------------------
Remove the file given in PATH. Return 0 for success and -1 in
case of an error.
Example: t.remove("temp-file");
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

251
t3x9r3-extended/tcdis.c Normal file
View File

@ -0,0 +1,251 @@
/*
* Tcode9 disassembler
* Nils M Holm, 2017, CC0 license
* https://creativecommons.org/publicdomain/zero/1.0/
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define MEMSIZE 131072
#define byte unsigned char
#define sbyte signed char
#define cell int
byte *M;
sbyte *S;
cell Red;
cell Data;
void writes(char *s) {
write(1, s, strlen(s));
}
void wlog(char *s) {
write(1, s, strlen(s));
}
void fail(char *s) {
wlog("tcdis: ");
wlog(s);
wlog("\n");
_exit(1);
}
#define BSIZE 1024
char B[BSIZE];
int Fd, C, K;
int rdch(void) {
if (C >= K) {
K = read(Fd, B, BSIZE);
C = 0;
if (K < 1) return -1;
}
C = C+1;
return B[C-1] & 255;
}
cell rdwd(void) {
cell v;
v = rdch();
v = v | (rdch() << 8);
v = v | (rdch() << 16);
v = v | (rdch() << 24);
return v;
}
void readblk(byte *v, int k) {
int i, c;
for (i=0; i<k; i++) {
c = rdch();
if (-1 == c) fail("file too short");
v[i] = c & 255;
}
}
void load(char *s) {
int c;
cell tp, dp;
Fd = open(s, O_RDONLY);
if (Fd < 0) fail("could not open program");
C = 0;
K = 0;
c = rdch();
while (c != '\n' && c != -1)
c = rdch();
if (rdwd() != 0x39583354) fail("not a tcvm program");
tp = rdwd();
dp = rdwd();
Data = tp;
Red = tp + dp;
M = malloc(MEMSIZE);
S = (sbyte *) M;
if (NULL == M) fail("not enough memory");
readblk(M, tp+dp);
if (rdch() != -1) fail("trailing garbage");
close(Fd);
}
cell A, F, I, P;
cell w(cell a) {
return M[a+0]
| (M[a+1] << 8)
| (M[a+2] << 16)
| (M[a+3] << 24);
}
#define a() w(I+1)
#define a2() w(I+5)
#define s() S[I+1]
#define s2() w(I+2)
void decode(void) {
cell j;
for (I=0; I < Data; I++) {
printf("%07x ", I);
switch (M[I]) {
case 0x80:
case 0x00: printf("push"); break;
case 0x81:
case 0x01: printf("clear"); break;
case 0x82: printf("ldval\t%x", s()); I++; break;
case 0x02: printf("ldval\t%x", a()); I += 4; break;
case 0x83:
case 0x03: printf("ldaddr\t%x", a()); I += 4; break;
case 0x84: printf("ldlref\t%x", s()); I++; break;
case 0x04: printf("ldlref\t%x", a()); I += 4; break;
case 0x85:
case 0x05: printf("ldglob\t%x", a()); I += 4; break;
case 0x86: printf("ldlocl\t%x", s()); I++; break;
case 0x06: printf("ldlocl\t%x", a()); I += 4; break;
case 0x87:
case 0x07: printf("stglob\t%x", a()); I += 4; break;
case 0x08: printf("stlocl\t%x", a()); I += 4; break;
case 0x88: printf("stlocl\t%x", s()); I++; break;
case 0x89:
case 0x09: printf("stindr"); break;
case 0x8a:
case 0x0a: printf("stindb"); break;
case 0x8b:
case 0x0b: printf("incglob\t%x %x", a(), a2()); I += 8; break;
case 0x8c: printf("inclocl\t%x %x", s(), s2()); I += 5; break;
case 0x0c: printf("inclocl\t%x %x", a(), a2()); I += 8; break;
case 0x8d: printf("alloc\t%x", s()); I++; break;
case 0x0d: printf("alloc\t%x", a()); I += 4; break;
case 0x8e: printf("dealloc\t%x", s()); I++; break;
case 0x0e: printf("dealloc\t%x", a()); I += 4; break;
case 0x8f:
case 0x0f: printf("loclvec"); break;
case 0x90:
case 0x10: printf("globvec\t%x", a()); I += 4; break;
case 0x91:
case 0x11: printf("index"); break;
case 0x92:
case 0x12: printf("deref"); break;
case 0x93:
case 0x13: printf("indxb"); break;
case 0x94:
case 0x14: printf("drefb"); break;
/* case 0x95: MARK */
/* case 0x15: MARK */
/* case 0x96: RESOLV */
/* case 0x16: RESOLV */
case 0x97:
case 0x17: printf("call\t%x", a()+I+5); I += 4; break;
case 0x98:
case 0x18: printf("jumpfwd\t%x", a()+I+5); I += 4; break;
case 0x99:
case 0x19: printf("jumpback\t%x", a()+I+5); I += 4; break;
case 0x9a:
case 0x1a: printf("jmpfalse\t%x", a()+I+5); I += 4; break;
case 0x9b:
case 0x1b: printf("jmptrue\t%x", a()+I+5); I += 4; break;
case 0x9c:
case 0x1c: printf("for\t%x", a()+I+5); I += 4; break;
case 0x9d:
case 0x1d: printf("fordown\t%x", a()+I+5); I += 4; break;
case 0x9e:
case 0x1e: printf("enter"); break;
case 0x9f:
case 0x1f: printf("exit"); break;
case 0xa0: printf("halt\t%x", s()); I++; break;
case 0x20: printf("halt\t%x", a()); I += 4; break;
case 0xa1:
case 0x21: printf("neg"); break;
case 0xa2:
case 0x22: printf("inv"); break;
case 0xa3:
case 0x23: printf("lognot"); break;
case 0xa4:
case 0x24: printf("add"); break;
case 0xa5:
case 0x25: printf("sub"); break;
case 0xa6:
case 0x26: printf("mul"); break;
case 0xa7:
case 0x27: printf("div"); break;
case 0xa8:
case 0x28: printf("mod"); break;
case 0xa9:
case 0x29: printf("and"); break;
case 0xaa:
case 0x2a: printf("or"); break;
case 0xab:
case 0x2b: printf("xor"); break;
case 0xac:
case 0x2c: printf("shl"); break;
case 0xad:
case 0x2d: printf("shr"); break;
case 0xae:
case 0x2e: printf("eq"); break;
case 0xaf:
case 0x2f: printf("neq"); break;
case 0xb0:
case 0x30: printf("lt"); break;
case 0xb1:
case 0x31: printf("gt"); break;
case 0xb2:
case 0x32: printf("le"); break;
case 0xb3:
case 0x33: printf("ge"); break;
/* case 0xb4: WORD */
/* case 0x34: WORD */
case 0xb5:
case 0x35: printf("syscall\t%x", M[I+1]); I++; break;
default: fail("invalId opcode"); break;
}
putchar('\n');
fflush(stdout);
}
putchar('\n');
for (; I < Red; I += 16) {
printf("%07x ", I);
for (j=0; j<16; j++) {
if (j && j%4 == 0) putchar(' ');
printf(" %02x", M[I+j]);
}
printf(" ");
for (j=0; j<16; j++)
printf("%c",
' ' <= M[I+j] && M[I+j] <= '~'? M[I+j]: '.');
putchar('\n');
}
}
cell main(cell argc, char **argv) {
if (argc != 2) fail("usage: tcdis program");
load(argv[1]);
decode();
return 0;
}

318
t3x9r3-extended/tcvm.c Normal file
View File

@ -0,0 +1,318 @@
/*
* Ad-hoc Tcode9 virtual machine
* Nils M Holm, 2017, CC0 license
* https://creativecommons.org/publicdomain/zero/1.0/
*
* Running at about 1/13th of the speed of native
* T3X9 output when compiled with clang -O2.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#undef DEBUG
#define MINSIZE 131072
#define STKLEN 65536
#define byte unsigned char
#define sbyte signed char
#define cell int
byte *M;
sbyte *S;
cell Red;
cell Memsize;
void writes(char *s) {
write(1, s, strlen(s));
}
void wlog(char *s) {
write(2, s, strlen(s));
}
void fail(char *s) {
wlog("tcvm: ");
wlog(s);
wlog("\n");
_exit(1);
}
#define BSIZE 1024
char B[BSIZE];
int Fd, C, K;
int rdch(void) {
if (C >= K) {
K = read(Fd, B, BSIZE);
C = 0;
if (K < 1) return -1;
}
C = C+1;
return B[C-1] & 255;
}
cell rdwd(void) {
cell v;
v = rdch();
v = v | (rdch() << 8);
v = v | (rdch() << 16);
v = v | (rdch() << 24);
return v;
}
void readblk(byte *v, int k) {
int i, c;
for (i=0; i<k; i++) {
c = rdch();
if (-1 == c) fail("file too short");
v[i] = c & 255;
}
}
void load(char *s) {
int c;
cell tp, dp;
Fd = open(s, O_RDONLY);
if (Fd < 0) fail("could not open program");
C = 0;
K = 0;
c = rdch();
while (c != '\n' && c != -1)
c = rdch();
if (rdwd() != 0x39583354) fail("not a tcvm program");
tp = rdwd();
dp = rdwd();
Red = tp + dp;
M = malloc(MINSIZE);
S = (sbyte *) M;
if (NULL == M) fail("not enough memory");
readblk(M, tp+dp);
if (rdch() != -1) fail("trailing garbage");
close(Fd);
}
cell A, F, I, P;
cell w(cell a) {
return M[a+0]
| (M[a+1] << 8)
| (M[a+2] << 16)
| (M[a+3] << 24);
}
void Sw(cell a, cell w) {
M[a+0] = w & 255;
M[a+1] = (w >> 8) & 255;
M[a+2] = (w >> 16) & 255;
M[a+3] = (w >> 24) & 255;
}
void push(cell x) {
P -= 4;
Sw(P, x);
}
cell pop(void) {
P += 4;
return w(P-4);
}
#define a() w(I+1)
#define a2() w(I+5)
#define s() S[I+1]
#define s2() w(I+2)
cell memscan(cell p, cell c, cell k) {
cell i;
for (i=0; i<k; i++)
if (M[p+i] == c)
return i;
return -1;
}
cell sys_call(cell n) {
cell r;
#ifdef DEBUG
printf("SYSCALL(%d): %x %x %x %x\n", n, w(P+12), w(P+8), w(P+4), w(P));
#endif
switch (n) {
case 0: r = memcmp(&M[w(P+12)], &M[w(P+8)], w(P+4)); break;
case 1: memcpy(&M[w(P+12)], &M[w(P+8)], w(P+4)); r = 0; break;
case 2: memset(&M[w(P+12)], w(P+8), w(P+4)); r = 0; break;
case 3: r = memscan(w(P+12), w(P+8), w(P+4)); break;
case 4: r = creat((char *) &M[w(P+4)], 0644); break;
case 5: r = open((char *) &M[w(P+8)], w(P+4)); break;
case 6: r = close(w(P+4)); break;
case 7: r = read(w(P+12), &M[w(P+8)], w(P+4)); break;
case 8: r = write(w(P+12), &M[w(P+8)], w(P+4)); break;
case 9: r = rename((char *) &M[w(P+8)], (char *)&M[w(P+4)]); break;
case 10: r = remove((char *) &M[w(P+4)]); break;
default: fail("bad system call"); r = 0; break;
}
return r;
}
void run(int k) {
int t;
Memsize = Red + k + STKLEN;
M = realloc(M, Memsize);
S = (sbyte *) M;
if (NULL == M) fail("not enough memory");
P = Memsize;
for (I = 0;; I++) {
#ifdef DEBUG
printf("F=%08x P=%08x I=%08x %2x a=%08x A=%08x S0=%08x\n",
F, P, I, M[I], a(), A, w(P));
#endif
if (P < Red) fail("stack overflow");
switch (M[I]) {
case 0x80:
case 0x00: push(A); break;
case 0x81:
case 0x01: A = 0; break;
case 0x82: A = s(); I++; break;
case 0x02: A = a(); I += 4; break;
case 0x83:
case 0x03: A = a(); I += 4; break;
case 0x84: A = F+s(); I++; break;
case 0x04: A = F+a(); I += 4; break;
case 0x85:
case 0x05: A = w(a()); I += 4; break;
case 0x86: A = w(F+s()); I++; break;
case 0x06: A = w(F+a()); I += 4; break;
case 0x87:
case 0x07: Sw(a(), A); I += 4; break;
case 0x88: Sw(F+s(), A); I++; break;
case 0x08: Sw(F+a(), A); I += 4; break;
case 0x89:
case 0x09: Sw(pop(), A); break;
case 0x8a:
case 0x0a: M[pop()] = (byte) A; break;
case 0x8b:
case 0x0b: t = a(); Sw(t, w(t)+a2()); I += 8; break;
case 0x8c: t = s(); Sw(F+t, w(F+t)+s2()); I += 5; break;
case 0x0c: t = a(); Sw(F+t, w(F+t)+a2()); I += 8; break;
case 0x8d: P -= s(); I++; break;
case 0x0d: P -= a(); I += 4; break;
case 0x8e: P += s(); I++; break;
case 0x0e: P += a(); I += 4; break;
case 0x8f:
case 0x0f: push(P); break;
case 0x90:
case 0x10: Sw(a(), P); I += 4; break;
case 0x91:
case 0x11: A = (A<<2) + pop(); break;
case 0x92:
case 0x12: A = w(A); break;
case 0x93:
case 0x13: A = A + pop(); break;
case 0x94:
case 0x14: A = M[A]; break;
/* case 0x95: MARK */
/* case 0x15: MARK */
/* case 0x96: RESOLV */
/* case 0x16: RESOLV */
case 0x97:
case 0x17: push(I+4); I += a(); I += 4; break;
case 0x98:
case 0x18: I += a(); I += 4; break;
case 0x99:
case 0x19: I += a(); I += 4; break;
case 0x9a:
case 0x1a: if (0 == A) { I += a(); } I += 4; break;
case 0x9b:
case 0x1b: if (0 != A) { I += a(); } I += 4; break;
case 0x9c:
case 0x1c: if (pop() >= A) { I += a(); } I += 4; break;
case 0x9d:
case 0x1d: if (pop() <= A) { I += a(); } I += 4; break;
case 0x9e:
case 0x1e: push(F); F = P; break;
case 0x9f:
case 0x1f: F = pop(); I = pop(); break;
case 0xa0: exit(s()); break;
case 0x20: exit(a()); break;
case 0xa1:
case 0x21: A = -A; break;
case 0xa2:
case 0x22: A = ~A; break;
case 0xa3:
case 0x23: A = 0==A? -1: 0; break;
case 0xa4:
case 0x24: A = pop() + A; break;
case 0xa5:
case 0x25: A = pop() - A; break;
case 0xa6:
case 0x26: A = pop() * A; break;
case 0xa7:
case 0x27: A = pop() / A; break;
case 0xa8:
case 0x28: A = pop() % A; break;
case 0xa9:
case 0x29: A = pop() & A; break;
case 0xaa:
case 0x2a: A = pop() | A; break;
case 0xab:
case 0x2b: A = pop() ^ A; break;
case 0xac:
case 0x2c: A = pop() << A; break;
case 0xad:
case 0x2d: A = (unsigned) pop() >> A; break;
case 0xae:
case 0x2e: A = pop() == A? -1: 0; break;
case 0xaf:
case 0x2f: A = pop() != A? -1: 0; break;
case 0xb0:
case 0x30: A = pop() < A? -1: 0; break;
case 0xb1:
case 0x31: A = pop() > A? -1: 0; break;
case 0xb2:
case 0x32: A = pop() <= A? -1: 0; break;
case 0xb3:
case 0x33: A = pop() >= A? -1: 0; break;
/* case 0xb4: WORD */
/* case 0x34: WORD */
case 0xb5:
case 0x35: A = sys_call(M[I+1]); I = pop(); break;
default: fail("invalid opcode"); break;
}}
}
int size(void) {
int k, n;
k = 0;
for (I = 0;; I++) {
n = 0;
switch (M[I]) {
case 0x8d: n = s(); I++; break;
case 0x0d: n = a(); I += 4; break;
case 0x90:
case 0x10: I += 4; break;
case 0x98:
case 0x18: I += a(); I += 4; break;
default: return k;
}
k += n;
}
return k;
}
cell main(cell argc, char **argv) {
if (argc != 2) fail("usage: tcvm program");
load(argv[1]);
run(size());
return 0;
}

47
t3x9r3-extended/test.t Normal file
View File

@ -0,0 +1,47 @@
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(10 mod i));
writes("\n");
end
end