Tinycat BASIC
=============


Tinycat BASIC is a line-number BASIC dialect that can be implemented in less than a thousand lines of Java, Go or D (barring extensions to the core). It achieves that by relying on direct interpretation and having no type system. Its name is meant to honor the classic dialect Tiny BASIC as inspiration and ancestry.

Rationale
---------

While the hardware limitations that made Tiny BASIC desirable are largely a thing of the past, having programming language dialects that can be *trivially* implemented remains a good idea. It reduces software dependencies and black box components.

You can also think of it as an art project, and/or a comparative study in programming language implementation.

Features
--------

Tinycat BASIC has a number of useful additions on top of its model:

- floating point numbers;
- arbitrary variable names;
- control structures like DO ... LOOP and FOR ... NEXT;
- the logical operators: AND, OR, NOT;
- power and flooring division operators;
- functions, both built-in and user-defined*;
- random number generation.

*) Note: the Go implementation lacks DEF FN.

Some features would *not* be trivial to add, and therefore outside the scope of this project:

- arrays;
- string variables.

Others can be added easily, but would cause more trouble than it's worth:

- multiple statements per line; they require littering the source code with special cases, and lower performance for little benefit.

Performance
-----------

Note: the figures below have changed repeatedly as the interpreters were improved and flaws were fixed in the benchmark itself. Consider them a rough indication of relative performance.

The reference Python implementation is 113 times slower than the host language. Conversely, the Go implementation is 47 times slower than native code, within limits for this type of interpreter. The D interpreter starts out 49 times slower than native code, but becomes only 43 times slower when optimized (it gains more from optimization than the native benchmark). In absolute terms however it's slower than Go by roughly 35%. Compilers other than `dmd` should yield better results.

(As of October 2021, there's support for compiling the Python prototype to native code with Cython; this gives a roughly 8.5x speed-up if type declarations are used; the Python code was not modified.) 

The Java implementation proved harder to benchmark, as a long-running interpreter runs progressively faster. That said, it seems to be roughly 47 times slower than pure Java on a fresh start (7 times slower than the Go implementation in absolute numbers), but ends up only 2.6 times slower than a compiled Java program. That's almost as if the interpreter wasn't in the way anymore, and actually three times faster than the Go implementation!

Overall, Java seems the best suited for interpreting another language. Or at least this interpreter architecture happens to suit Java unusually well.

Embedding Tinycat BASIC
-----------------------

Most editions of the interpreter can be embedded, with some caveats:

- the Python edition will only have one shared context;
- the D edition doesn't have its own `main` function;
- the Go edition would require editing, and even then with limitations.

The D and Java interpreters support per-context RNGs and I/O redirection.

Extending Tinycat BASIC
-----------------------

The D and Java implementations are fully extensible: by subclassing the interpreter, you can add more statements, built-in functions and even expression syntax!

The Python implementation can be extended with new statements or functions.

In the Go implementation, you can only add more built-in functions without changing the source code, even if you convert it to an importable package.

Supported commands
------------------

	LIST
	RUN
	CONTINUE
	CLEAR
	NEW
	DELETE line-number
	LOAD "filename"
	SAVE "filename"
	BYE

Commands are only available at the built-in command prompt. It is assumed that a program embedding the interpreter will provide its own alternatives.

The BYE command leaves the command loop and returns to the host application (which simply closes a stand-alone interpreter). You can also press Ctrl-D to send an end-of-file character.

Supported statements
--------------------

	LET name "=" expression
	IF expression THEN statement
	GOTO expression
	PRINT (string | expression)? ("," (string | expression))* ";"?
	INPUT (string ",")? name ("," name)?
	FOR name = expression TO expression (STEP expression)?
	NEXT name
	GOSUB expression
	RETURN
	DO
	LOOP (WHILE | UNTIL) expression
	REM text
	DEF FN name "(" (name ("," name)?)? ")" "=" expression**
	RANDOMIZE expression?
	STOP
	END
	
**) Note: absent in the Go edition.

Built-in functions
------------------

	TIMER()
	RND()
	PI()
	INT(n)
	ABS(n)
	SQR(n)
	SIN(n)
	COS(n)
	RAD(n)
	DEG(n)
	MIN(a, b)
	MAX(a, b)
	MOD(a, b)
	HYPOT2(a, b)
	HYPOT3(a, b, c)
	IIF(a, b, c)
	
Beware that the IIF function doesn't short-circuit (and neither do the logical operators).

Expression syntax
-----------------

	expression ::= disjunction
	disjunction ::= conjunction ("or" conjunction)*
	conjunction ::= negation ("and" negation)*
	negation ::= "not"? comparison
	comparison ::= math_expr (comp_oper math_expr)?
	comp_oper ::= "<" | ">" | "<=" | ">=" | "<>" | "="
	
	math_expr ::= term (("+"|"-") term)*
	term ::= factor (("*" | "/" | "\") factor)*
	factor ::= ("+"|"-")? (number | name | funcall | "(" expression ")")
	funcall ::= name ("(" expr_list? ")")?
	expr_list ::= expression ("," expression)*

Bugs and caveats
----------------

Java always displays numbers with at least 6 digits of precision. It seems to be a bug in `String.format()`.

Python always displays numbers with *at most* six digits of precision -- the exact opposite behavior!

For portability, programs making use of randomness should call RANDOMIZE near the beginning. Not all implementations do that by default.

The Go implementation is abandoned as of October 2018.

License and support
-------------------

Tinycat Basic is free and open source software under the Artistic License 2.0; see the included text file for details. Questions and feedback can be sent to <felix@plesoianu.ro>.

If you find Tinycat Basic useful, fun and/or educational, please send a small donation to <https://paypal.me/notimetoplay>. Thank you.
