Calico Basic

From IPRE Wiki
Jump to: navigation, search

Calico Basic is based on a program by Andrea Griffini,

Calico Basic supports all Calico Modules and has access to the global "calico" object (see Calico: calico object for more information on that).

Supported Basic commands


Wipes out the current program from memory.

LOAD <file name>

Loads a basic program in memory. If this command is issued from a program then the loaded program will also be started (variables are NOT cleared in this case).

SAVE <file name>

Saves the basic program from memory to the given filename.


Clears all variables and gosub/for stacks.

PRINT <expr> [ ; <expr> ] {;}

Video outputs of a sequence of expressions separated by ";". A final ";" means that no linefeed should be sent at the end.

REM ...

Does nothing and eats anything up to the end of the line.

GOTO <line>

Transfers execution at the specified program line.


Lists the program currently in memory.


Renumbers the program currently in memory. Starts at line 100 and increments by 10.


Clears all variables and starts the program from the first instruction.

IF <expr> THEN <stmt> [ : <stmt> ... ]

If the condition is false then execution continues on next line. Otherwise execution continues on the same line with what follows "THEN". If what follows "THEN" is just a number then the parser assumes a "GOTO" statement. Sorry... no ELSE or multiline IF.

GOSUB <line>

Jumps at the subroutine at line xxx remembering where we are now.


Returns from a subroutine and continues with the statement after the GOSUB command that invoked it.

FOR = <start_expr> TO <stop_expr> { STEP <inc_expr> }

Begins a loop in which the specified variable will start from the value of start_expr and will end with the value of stop_expr, incrementing the value at every iteration by inc_expr. Note that the first iteration (with value start_expr) will be ALWAYS done, even if the value of stop_expr is lower and inc_expr is positive. If step_expr is omitted then the parser assumes "STEP 1".


Makes a new iteration of last opened FOR or closes the FOR if the incremented variable reached the end of the loop. After incrementing the variable the execution resumes at the statement following the FOR statement.


Stops the program execution and returns to the interactive prompt.

INPUT { <prompt> ; }

Asks the user for a value to put in the specified variable, first displaying the prompt. If no prompt is provided then the parser assumes "?".

IMPORT <library name>

Imports a library (either a Calico Module, or a standard Python library).

You can then use the library with dotted names. For example:

win = Myro.Window("Title", 500, 200)

You also have access to all of the common Python libraries:

IMPORT "random"
r = random.random()


Variables names must match /[a-zA-Z_][a-zA-Z_0-9]*[%$]?/, i.e. they must start with a letter or an underscore, may contain letters, digits or underscore and may optionally end with "%" or "$".

Strings are double-quote delimited, with no provision for escape codes.

Predefined functions

  • LEN(s$) length of a string
  • STR$(x) number->string conversion
  • VAL(s$) string->number conversion
  • LEFT$(s$,n) first n characters of a string
  • RIGHT$(s$,n) last n characters of a string
  • MID$(s$,x,n) n characters of a string starting from position x
  • INT(x) float->int conversion
  • ABS(x) absolute value
  • SIN(x) sine of x
  • COS(x) cosine of x
  • TAN(x) tangent of x
  • ATN(x) arc-tangent of x
  • SQR(x) square root of x
  • LOG(x) natural logarithm of x
  • EXP(x) exponential of x
  • TIME(x) current unix time (argument ignored)
  • RND(x) random number 0 < RND(x) < 1 (argument ignored)

Note that passing a wrong number of arguments is an error that will be caught at *runtime* (and by python, using also an ugly error message). There is no provision for user-defined functions... use subroutines instead.


Binary operators (listed by priority) are:

  • * / MOD Multiplication, division, modulo
  • + - Addition, subtraction
  • <= < >= > <> = Comparision
  • AND Bitwise AND
  • OR Bitwise OR
  • XOR Bitwise XOR

All comparision operators return 0 on false or -1 on true.

Available unary operators are NOT (logical not) and unary minus. They have a priority *higher* than any binary operator.

Unsupported BASIC features

A lot :-) ... actually most of them... the most annoying and evident is probably missing support for arrays (!!!!).

Differences from BASIC

Because I'm lazy the language is indeed "typeless" ... so variable names may end with "%" or "$", but that character has no special meaning.


There is tokenizer that recognizes identifiers, numbers, strings and special characters (or two-character sequences like '>=').

The parser for expressions is a recursive-descent explicitly coded and the result is stored in a tree of python class instances. The tree is traversed for expression evaluation.

The parser for statements is non recursive (this BASIC is unstructured) and stores the result in a single list of (line,stm) pairs. The line may be -1 if the statement is a continuation (":") on the previous line. The parsing of IF...THEN is hacked so there is no need of ":" after the "THEN" even if that would have been more uniform for this structure. "LIST" is hacked too to avoid generating annoying ":".

There are two stacks (one for GOSUBs and one for FORs) and one dictionary for variables. There is also a dictionary for mapping line numbers to indexes in the statement list.

All this is a somewhat strange mix. Normally compilers I wrote were either interpreting directly a parsed tree (with both statements and expressions being nodes of the tree) OR contained a virtual machine and BOTH statements and expressions where compiled into instructions for the VM.

Yes... I agree with you this mix is uglier than either.

One strange side-effect is that I can get back a listing from the result of parsing, but because that for example for expressions only the resulting tree is stored, the parenthesis that have been used for grouping will disappear, but a lot of parenthesis will be added. So if you enter:

  100 x = (((1))): y=-2

you will get in the listing something different...

  100 X = 1 : Y = -(2)

Also other syntax changes may appear in listing, for example...

  100 if x=3 then 220
  110 for x=1 to 10

will appear as

  100 IF (X = 3) THEN  GOTO 220
  110 FOR X=1 TO 10 STEP 1

because also statement objects don't remember exactly the text they were parsed from.

In Apple ][ BASIC the approach was different. What it was stored was just the sequence of tokens... everything else was done at runtime doing for example expr evaluation *while* parsing the sequence of tokens.

Calico Extensions

  1. variables are case-sensitive
    1. X = 2
    2. x = 1
    3. if x != X:
    4. print("Yes, they are different")
  2. added IMPORT "Library", IMPORT "module"
    1. IMPORT "Graphics"
    2. d = Graphics.Dot(10, 10)
    3. IMPORT "math"
    4. x = math.sqrt(56)
  3. function call and variable evaluation outside of assign
    1. IMPORT "Graphics"
    2. Myro.init("COM8")
    3. Myro.robot
  4. Access to global "calico" instance
    1. calico.Execute("(= 2 3)", "scheme")

Calico Extension Ideas

  • Add missing standard Basic functionality
  • Add lists (arrays) and dictionaries
  • Add ability to make functions
  • Add ability to make classes
  • Add access to shared DLR environment
  • Add ability for outside languages to call into Calico Basic (other than calico.Execute(code, "basic")?)