Description of yabasic
Author: Marc-Oliver Ihm

Yabasic Yet another Basic for Unix and Windows | Version 2.72
Yabasic under Unix
Invocation, Options Setting Defaults
Yabasic under Windows
Invocation, Options Setting Defaults
Yabasic by examples
A simple Program with input and print Arithmetic: Operators and Functions Making decisions: The if-statement and the select-statement Strings and loops (for, repeat, while and do) User defined subroutines, defining subs at runtime and libraries Graphics, printing and Bitmaps | Data and Arrays and More on Arrays Files and more on input Interaction with the Operating System Peek and Poke Advanced Printing: Positions, Formats and Colour Loose Ends
Index
Index of Keywords Index of Concepts
Background
Copyright
Choose between the Artistic License and the Gnu General Public License.

This document describes the features of yabasic. In short, yabasic implements the most common (and simple) elements of the basic-language, plus some graphic facilities; anyone, who has ever written basic-programs should feel at home.

This page covers all the features of yabasic, you don't need any other text to learn it. In fact, there is no other text about yabasic, neither a unix-man-page nor a Windows-helpfile.

This text doesn't teach basic from scratch, it rather assumes some experience with the basic-programming-language.

# Unix

## Invocation

There are three way to start yabasic:

1. | You may write your basic-program to a file (e.g. foo.yab) and call yabasic with this file as an argument: yabasic foo.yab this will make yabasic execute your program.
2. | You may start you yabasic without any filename. Typing yabasic makes yabasic start and prompt for commands to execute; after you have typed in your code, press RETURN twice and yabasic will execute your commands. This behavior allows yabasic to be used as some sort of fancy desktop calculator.
3. | You may put your program into a file and insert this text as the very first line: #!/usr/bin/yabasic This is only an example and you should substitute for /usr/bin/yabasic the full pathname of yabasic on your computer. Yabasic will treat the line starting with #! as a comment, but Unix will invoke yabasic to execute this program.

Back to table of contents ...

## Options

-h
  Prints out a short help message; -help or -? are accepted as well.
-fg foreground-color
  Sets the foreground color for graphics. The usual X colornames like red, green are accepted.
-bg background-color
  Sets the background color.
-geometry geometry-string
  The usual X geometry-string will work (e.g. +10+10), but any window size will be ignored.
-display Name-of-Display
  Name of the Display, where the window should appear.
-font Name-of-font
  Name of the font, which will be used for graphics text.
-execute Yabasic-Commands
  Execute specified commands right away.
-i
  Sets the initial infolevel. This controls the amount of information one gets about the progress of program execution, Every level contains all lower levels (e.g. w contains f and e) and can be one of:
  d
    Set the infolevel to debug : This gives detailed debugging information; much more output than you'd probably like to read.
  n
    note : Useful information; e.g. about execution time and memory consumption.
  w
    warning : Gives you warnings, that something has gone wrong (e.g. division by zero); nevertheless execution proceeds.
  e
    error : A serious error (e.g. an array boundary violation) has occurred, stopping the program.
  f
    fatal : Something has gone wrong and cannot be fixed; the interpreter exits immediately. This happens most often in the course of an arithmetic fault (floating point exception) but can also be a sign of an internal error within yabasic.
  The default infolevel is w.
-licence
  This makes yabasic print out its copyleft; have a look and you will see, that almost anything is allowed.
-doc name
  Prints the embedded documentation of the given yabasic program or library
-librarypath path

Changes the directory where yabasic searches any library which is not found in the current directory.

Back to table of contents ...

## Setting defaults

The colors, text-font and the window position should be set on the command-line , or specified in the users resource file (this is usually the file .Xresources in your home-directory); e.g.:

  yabasic*foreground: blue

      yabasic*background: gold
      yabasic*geometry: +10+10
      yabasic*font: 9x15

This sets the foreground of the graphics-window to blue, the background to gold, the window will appear at position 10,10 and the text-font will be 9x15.

Back to table of contents ...

# Windows

## Invocation

After you have run the setup program, yabasic can be invoked in three ways:

1. | Choose "yabasic" from the start-menu: Yabasic will come up with a console window and wait for a program to be typed in right away.
2. | Click with the right mousebutton on your desktop. Choose "new" from the context-menu that appears; this will create a new icon on your desktop. The context-menu of this icon has three entries "Execute", "Edit" and "View docu" (which shows the embedded documentation, if any); a double-click executes the program.
3. | Create a file containing your yabasic-program. This file should have the extension ".yab". Double-click on this file then invokes yabasic, to execute your program.

Back to table of contents ...

## Options

-h
  Prints out a short help message; -help or -? are accepted as well.
-geometry geometry-string
  E.g. +20+10 will place the graphic-window 10 pixels below and 20 pixels left of the upper left corner of the screen.
-font Name-of-font
  Name of the font which will be used for graphics text. Can be any of:
  decorative, dontcare, modern, roman, script, swiss
  You can append (without space) a fontsize to any of these identifiers, i.e. swiss30 chooses a swiss font, 30 pixels high.
-execute Yabasic-Commands
  Execute specified commands right away.
-i
  Sets the initial infolevel. This controls the amount of information one gets about the progress of program execution, Every level contains all lower levels (e.g. w contains f and e) and can be one of:
  d
    Set the inoflevel to debug : This gives detailed debugging information; much more output than you'd probably like to read.
  n
    note : Useful information; e.g. about execution time and memory consumption.
  w
    warning : Gives you warnings, that something has gone wrong (e.g. division by zero); nevertheless execution proceeds.
  e
    error : A serious error (e.g. an array boundary violation) has occurred, stopping the program.
  f
    fatal : Something has gone wrong and cannot be fixed; the interpreter exits immediately. This happens most often in the course of an arithmetic fault (floating point exception) but can also be a sign of an internal error within yabasic.
  The default infolevel is w.
-licence
  This makes yabasic print out its copyleft; have a look and you will see, that almost anything is allowed.
-doc name
  Prints the embedded documentation of the given yabasic program or library.
-librarypath path

Changes the directory where yabasic searches any library which is not found in the current directory.

Back to table of contents ...

## Setting defaults

To choose the default-values for graphic-font, fontsize and window position, you have to edit the registry.

Yabasic stores its defaults under:

  HKEY_LOCAL_MACHINE/SOFTWARE/Yabasic

You may edit the subkeys "font" and "geometry"; these subkeys accept the same values as the corresponding command line options -font and -geometry. Command line options take precedence over registry defaults.

Back to table of contents ...

# Yabasic explained by examples

## A simple Program

This is the first example:

  REM this is the first yabasic-program

      input "Enter two numbers:" a,b
      print a,"+",b,"=",a+b
      print "Please enter your Name:";
      INPUT a$
      print "Hello ",a$," !"

This program produces the following output (user input is displayed like this):

  Enter two numbers: 2 3

      2+3=5
      Please enter your Name: Bill
      Hello Bill !

This simple program contains three different commands:

REM
  The REM-statement introduces comments; everything after REM up to the end of the line is ignored.
input
  This statement reads one or more variables from the user. The optional prompt-string after the input-statement ("Enter a number:") is printed on the terminal prior to reading any input. Note that there is no semicolon after this prompt-string. To learn more about, how input chops a line into pieces you may refer to the section More on Input. To learn, how to read input from the keyboard without delay, check out Getting a key from the keyboard.
print
  The print-statement writes all its arguments to the screen; after writing its last argument, print goes to the next line (as in print "Hello ",a$," !"); to avoid this automatic newline, place a semicolon (;) after the last argument (as in print "Please enter your Name:";). To insert a tabulator instead of the automatic newline append a colon (,), e.g. print "Please enter your Name:", . Note that print can be abbreviated with a single question mark (?). If you want to print (or input) at a specific location, you may go to the section Prining on your Screen.

Furthermore some general properties of yabasic should be noted:

Case
  Commands can be entered in any case: input is the same as INPUT and even as InPUt. This applies to every command in yabasic but not to variables, i.e. a$ and A$ are different variables.
Variables
  Variable names are case sensitive (i.e. types of variables: a$ and A$ are different) and can be of any length. There are two sorts of variables:

String variables
  e.g. a$, b12$ or VeryLongName$ may contain strings of any length. String variables always have a Dollar-sign ($) as the last character of their names.
Numerical variables
  e.g. a, c3po or ThisIsAnEvenLongerName contain real numbers like 2, -1.3, 15.3e44 or 0.

  Variables (with the exception of arrays) need not be declared, their initial values are "" (for string variables) and 0.0 (for numerical variables).

Back to table of contents ...

## Arithmetic

### Operators

Yabasic has five arithmetic operators: + (addition), - (subtraction), * (multiplication), / (division) and ^ (power); they all behave as expected, i.e. this line of code

  print 1+2,2*3,4/2,2^3

produces this line of output:

  3 6 2 8

Note that the power operator (^) handles fractional powers: 8^(1/3) gives 2 as a result.

### Functions

This section demonstrates and explains the arithmetic functions of yabasic.

Trigonometric functions:
  There are 6 trigonometric functions:
    print sin(1.0),cos(pi),tan(3)
    print asin(0.5),acos(0.7)
    print atan(2),atan(1,2)
  These lines produce this output:
    0.841471 -1 -0.142547
    0.523599 0.795399
    1.10715 0.463648
  As you can see yabasic can calculate sine, cosine, tangent and their inverses. And, if you have an eye for trigonometry, you may have noticed that all these functions expect their argument in radians; to facilitate the transformation from degrees to radians (radian=degree*pi/180), there is a predefined variable named pi (or PI) which has an initial value of 3.14159.
  Finally note that the atan()-function comes in two flavors: Called with a single argument (e.g. atan(2)) atan()returns a value between -pi/2 ... +pi/2. Called with two arguments (e.g. atan(2,-1)) atan() returns a value between -pi and +pi; (This can be useful e.g. when transforming from cartesian to polar coordinates).
Exponentiation:
  The exp() functions comes with its inverse. the log()-function:
  print exp(1),log(2),log(euler)
  log() and exp() operate with the base e (=2.17828), which comes as a predefined variable named euler. Knowing this you won't be surprised to get the following output:
  2.71828 0.693147 1
Integer and fractional parts:
  The functions int() and frac() split their argument at the decimal point:
  print int(2.34),frac(2.34) produces: 2 0.34
Absolut values and signum
  The abs() and sig() functions return the absolute value and the signum of their arguments:
  print abs(-2.34),abs(2.34),sig(-2.34),sig(0),sig(2.34) produces: 2.34 2.34 -1 0 1
Remainder
  To get the remainder of a division employ the mod()-function; e.g. mod(11,4) produces 3, because when dividing 11 by 4 you get 2 and a remainder of 3.
Minimum and Maximum:
  Return the lower and higher value of their two arguments:
  print min(2,3),max(2,3) gives: 2 3
Square root and square:
  The square root is calculated by sqrt(), the square by sqr():
  print sqrt(2),sqr(2) gives 1.41421 4
Hexadecimal and binary numbers:
  To convert a decimal number to hex and vice versa, use hex$() and dec():
  print hex$(255)," is ",dec("ff") gives ff is 255
  Binary numbers can be converted quite similar to decimal: bin$(8) returns "100" and dec("100",2) will return 8. Note, that you need to supply the base (here: 2) with the dec()-function if it is different from the default value 16.
Random numbers:
  are returned by the ran()-function; this function comes in two flavours: Called without arguments (e.g. print ran()) you will get a random number between 0 and 1. Called with a single argument (e.g. print ran(2)) you will get a random number between 0 and the supplied argument.
  The ran()-function of yabasic uses the ran()-function of the C standard library, so you had better not expect too much randomness ...
Bitmanipulation:
  You may use the bitwise operations and(), or(), eor() (which might be written as xor()) like this:
  print and(10,7),or(9,3),eor(15,4)
  Which will give you 2 11 11 as a result. Note however, that yabasic has no not() operation; this is due to the way, yabasic treats numbers (there are no real integers). However, the same result can easily be achieved by using eor() with an argument of all ones (in binary): eor(255,123) eor(65535,266) are examples.

Back to table of contents ...

## Making decisions: The if-statement

To make decisions you have to use the if-statement:

>     input "Please enter a number" a
>     if (a>10) then
>       print "Your number is bigger than 10"
>     elsif (a>5) then
>       print "Your number is bigger than 5  but less or equal 10"
>     else
>       print "Your number is less or equal 5"
>     endif

As you can see, the condition has to be enclosed in parentheses (...). The else and the elsif-part of the if-statement are optional and can be omitted, as in this example:

>     input "Please enter a number" a
>     if (a>10 and a<20) then
>       print "bigger than 10":print "but less than 20"
>     fi

Note that endif can be written as fi too.

Just in case you want to write even less, you may try:

>     input "Please enter a number" a
>     if (a>10 and a<20) print "bigger than 10 ":print "but less than 20"

Note, that then and endif (or fi) have been omitted; As you might expect, both print-statements are executed or not, depending on the condition. Note however, that previous versions of yabasic behaved differently (and quite counterintuitive) in this respect.

Next, have a look at the condition (a>10 and a<20) of the if-statement:

Conditions:
  Numbers or arithmetic expressions can be compared with the usual relational operators: = (equal), <> (not equal), < (less than), <= (less or equal), > (greater than) and >= (greater or equal).
  Strings can be compared with just the same set of operators, where characters are ordered according to the ascii-charset; e.g. ("a"<"b") is true (because "a" precedes "b" within the ascii-charset) and likewise ("a"="b") is false.
  More than one comparison can be combined with parentheses () and these keywords: or, and, not; Note that not precedes and, which in turn precedes or (in the same way as * precedes + within arithmetic expressions).
  There are some other statements, which can be used within a condition:
  - you may use an open-statement as a condition to check, if it has been successful.
  - eof() checks, if a file contains more input.
    glob$() checks, if its first argument matches the pattern supplied as the second argument.

  For Convenience you may store the result of a comparison within a variable and retrieve it later:

      input "Please enter a number between 1 and 10: " a
      okay=(a>=1 and a<=10)
      if (not okay) print "autsch !"

  The result of the comparison is stored within the variable okay which is simply queried within the if-statement.

  The above example can be rewritten, showing the special constants TRUE and FALSE:

      input "Please enter a number between 1 and 10: " a
      if (a>=1 and a<=10) then okay=TRUE : else okay=FALSE : fi
      if (not okay) print "autsch !"

Multiple commands on one line
  Note that more than one command can appear on one line, as in
  print "bigger than 10":print "but less than 20"
  as long as you separate them with colons (:).
Comparing with many values
  Sometimes you may want to test an expression against many different values, e.g.: while processing user-input:

      input "Please enter your choice" a$

      switch a$:
        case "q":end
        case "?":case "h": print "Sorry, no help available" :break
        case "p": print "Howdy !"
      end switch

  This program checks its input (in the variable a$) against many different values, reacting accordingly. Of course you may do this with an if-statement, but it would be much harder to understand.

  Note finally, that you may just as well switch on arithmetic expressions.

Back to table of contents ...

## Strings and loops

Basic has always been simple and strong in string-processing; and yabasic also tries to continue in this tradition:

  input "Please enter a word" a$

      for a=len(a$) to 1 step -1:print mid$(a$,a,1);:next a
      print " is ",a$," reversed !"

If you try this program, you will get this output:

  Please enter a word: hello

      olleh is hello reversed !

for-next-loops
  The heart of the above program is the for-loop: everything from for to next is repeated, while the variable (a) goes from its initial value len(a$) to its final value 1. As you might have anticipated, len(a$) returns the length of its string-argument.
  Note the step-clause: the number after step (here: -1) is added to a after every repetition; in the example the step-clause makes a go down with every iteration. If you omit the step-clause, step 1 is assumed. Finally note, that you can leave the for next loop at any time by simple goto. You may put the for-loop to some offbeat usage; e.g. for a=1 to 100 step a:print a:next a prints the powers off 2 up to 64.
repeat-until loop, while-wend loop and do-loop loop
  Of course this for-loop can be reformulated as repeat-until, while-wend or do-loop:

      input "Please enter a word" a$

      a=len(a$):repeat print mid$(a$,a,1);:a=a-1 until(a=0)
      print " is ",a$," reversed !"

      input "Please enter a word" a$
      a=len(a$):while(a>0) print mid$(a$,a,1);:a=a-1 wend
      print " is ",a$," reversed !"

      input "Please enter a word" a$
      a=len(a$):do: print mid$(a$,a,1);:a=a-1:if (a=0) then break fi: loop
      print " is ",a$," reversed !"

  All these loops produce the same result (as long as you input at least one character !).
break and continue
  The last example also shows the break-statement, which can be used to break out of any loop:

      do

        a$=inkey$
        if (a$="q") break
        if (a$=" ") continue
        print a$
        loop

  The above example reads characters from the keyboard and prints them to screen; however: If you enter "q" the loop is terminated (by the break-statement) and if you enter " " (space) the loop starts all over without printing (caused by the continue-statement)

Within the for-next-loop above the string-functions len() and mid$() are applied, but there are many more string functions:

Getting pieces out of a string:
  There are three functions which give back parts of a string:
  a$="123456"
  print left$(a$,2),"-",mid$(a$,2,3),"-",right$(a$,3)
  gives you the following output:
  12-234-456
  As you see left$() cuts off as many characters as specified by its second argument from the left of your string. right$() cuts from the right, and mid$() cuts in the middle, where the second argument is the starting point and the third one is the length of the string to be cut out; if you omit the third argument, mid$() returns everything up to the end of the string: mid$("Hallo",2) returns "allo".
  Furthermore mid$() and its friends can even be used to selectively change parts of a string:
  a$="123456":left$(a$,2)="abcd":print a$
  results in
  ab3456
  As you see only the two leftmost characters are changed (even though the string "abcd" contains four characters); the same can be done with mid$() or right$(). Note that assigning to left$()/right$()/mid$() will never change the length of the string.
strings to numbers (and reverse):
  The function str$()converts its numeric argument to a string:
  print str$(12) gives the string "12" as a result. The formatting of the number can be influenced by an optional second argument: print str$(12.123455,"##.##") returns the string 12.12. The second argument has the same effect as the format of the print using statement.
  Just the opposite is done by the function val(): print 2+val("23") gives 25 as a result, whereas print val("e2") delivers 0 (because "e2" is not a valid number).
The ascii-charset:
  yabasic offers two functions to work with the ascii-charset. asc() gives you a specific ascii-character: print asc("e") gives 101 as a result, because the character "e" has position 101 within the ascii-charset. Likewise the function chr$() returns the ascii-char for a given position within the charset, e.g. chr$(98) returns "b".
Escape-sequences
  Nevertheless you won't use chr$() as often as you might think, because the most important nonprintable characters can be constructed using escape-sequences with the \-character: You might use \n instead of chr$(10) wherever you want to use the newline-character.
  Finally escape sequences of the form \xHEX allow to encode arbitrary characters: E.g. \x012 returns the character chr$(18). Note that \x requires a hexadecimal number; and hexadecimal 12 is the same as decimal 18.
  The following table lists all escape sequences of yabasic (of course, these are just the sequences known within the C-language):

  Escape-sequence | Resulting Char
  \n | newline
  \t | tabulator
  \v | vertical tabulator
  \b | backspace
  \r | carriage return
  \f | formfeed
  \a | alert
  \\ | backslash
  \` | single quote
  \" | double quote

  These escape sequences are replaced within every pair of doublequotes (""), i.e. within literal strings; user input read with the input-statement is not affected in any way.
  Finally note, that escape sequences have a profound impact, when specifying Window-pathnames.

Here is another example which introduces the rest of yabasic's string-functions:

      label again
      print "Please enter a string containing the word \"yabasic\""
      input a$
      if (instr(lower$(a$),"yabasic")<>0) then
        gosub thanx
      else
        print "No, please try again !"
      endif
    goto loop
    label thanx
    print "Thanks a lot !"
    return

If you run this program you will receive the following output:

  Please enter a string containing the word "yabasic"

      ?thequickbrownfox
      No, please try again !
      Please enter a string containing the word "yabasic"
      ?jumpedyabasicoverthelazydog
      Thanx.

Marking locations in a program
  The first line in the example-program (label loop) is a label: As yabasic has no line-numbers, you need labels to mark a specific location within your program. You can compose labels out of letters and digits; the keyword label is required and the label itself should be unique within your program. Note that yabasic allows for line numbers too.
Jumping around in your program
  A label by itself causes no special action. Only in conjunction with the goto-statement (or gosub or restore) does a label have any function. If yabasic encounters a goto-statement (here: goto loop) then it searches for the matching label (here: label loop) and proceeds to execute at the position of the label.
  Note that you can even leave (and enter !) a for-next loop with goto.
  Closely related to the goto-command is the gosub-command; if yabasic encounters a gosub-statement then it searches for the matching label (label thanx in the example) and proceeds with execution at the position of the label, until it finds a return-statement. return makes yabasic return to the position of the original gosub and proceed from there.
  Note that both goto and gosub can be used as on goto and on gosub.
Finding strings in strings
  The example program above checks whether the user input contains the string "yabasic"; this is done with the help of the instr()-function; instr() gives back the position of its second string-argument within the first or zero, if it can't be found. E.g. instr("Hallo","al") gives back 2, because "al" appears at position 2 within "Hallo"; whereas instr("Hallo","Al") returns 0, because "Al" is not contained in "Hallo" (the case doesn't match).
  Furthermore: you may supply a third argument to the instr()-Function, which specifies the position from where the substring will be searched: instr("aloha","a") returns 1, whereas instr("aloha","a",2) returns 5, because searching the first "a" in "aloha" after position 2 is at position 5.
  Finally: There is an rinstr()-function which starts to search at the right end of the string and goes to the left. rinstr() has a three argument form too.
Changing the case of strings
  The sample-program contains some further string-functions: lower$() and its counterpart upper$() convert their string-argument to all lower or all upper case characters respectively, i.e. lower$("aBcD12fG") gives back "abcd12fg".
Removing spaces
  ltrim$() and rtrim$() are two functions to remove leading or trailing spaces from a string, e.g. ltrim$(" foo ") gives "foo " and rtrim$(" foo ") gives " foo". Finally, trim$() is the same as rtrim$(ltrime$()).
Splitting a string into tokens
  There are two handy functions to split a string into tokens; each function expects these parameters: a string which is to be split into tokens and an array which receives the individual tokens. An example would be:

      l$=" one two three "
      dim words$(1)
      num=token(l$,words$())
      for a=1 to num:print words$(a):next a

  Running this program gives:
    one
    two
    three
  Note, that token() automatically resizes the array words$() as needed; the old contents of the array is discarded. Furthermore you must not supply any indices when supplying the array (i.e. a$(2) is wrong, a$() is okay). If you supply a third argument (e.g. token(a$,words$(),":;")), the tokens are split at the characters given in the third string.
  Closely related with token() is the split() function. The program:

      l$="::one::two:three::four"
      dim words$(1)
      num=split(l$,words$(),":")
      for a=1 to num:print "Token: ",words$(a):next a

  will produce this output:

    Token:
    Token:
    Token: one
    Token:
    Token: two
    Token: three
    Token:
    Token: four
  split() focuses on the separator (":" in the example); if there are n separators in your string, split() will return exactly n+1 tokens (unless the string is empty, in which case you don't get any tokens). split() returns what is found between two occurrences of the separator, even if the resulting token is empty.
  token() on the other hand focuses on the tokens and may skip one or more separators, if there are no other characters in between. token() does not give back an empty token as long as there are characters left in the string.
Globbing
  glob() checks, if its first argument matches the pattern supplied as the second argument. This second argument may contain the special characters ? and *, whereas ? matches any single character and * matches zero or more arbitrary characters. Glob can only be used within conditions (e.g. after if, while or until) as in if (glob("Hallo","H*o")) print "Matches !". Some examples:

  glob-condition | True/False ?
  --- | ---
  glob("abcd","abc?") | true
  glob("abab","*") | true
  glob("abc","ab??") | false
  glob("abcdabab","ab*ab") | true
  glob("abcd","*a") | false

Back to table of contents ...

## User defined subroutines

One day you won't be satisfied with yabasic's builtin functions and commands; this will be the time to define and use your first subroutine:

> print multiply$("Hello",3)
> sub multiply$(a$,a)
> local b$,b
> for b=1 to a:b$=b$+a$:next b
> return b$
> end sub

This program simply prints the string HelloHelloHello which is (sort of) Hello multiplied with three. As there is no function in yabasic to "multiply" strings, the program defines it's own function multiply$() (starting with sub multiply$(a$,a)). multiply$() is a user defined string-function; you can tell from the $-sign in multiply$(), that it returns a string. multiply$() takes two arguments, a string and a number, which are passed to the function through the variables a$ and a. These variables, as well as b$ and b (defined in local b$,b), are local to the function: They are initialized as the subroutine is entered and keep their value just as long as the subroutine is executed. The computation within the subroutine is straightforward and the result is returned with the statement return b$.

Instead of return b$ you might just write return or omit the return-statement altogether; in this case an empty string (or 0.0 for numerical functions) would be returned; of course, this wouldn't be very useful for this subroutine.

Here are some examples showing, that yabasic is quite tolerant with user defined subroutines:

> print sum(1,4),sum(2),sum()
> sum(2,3)
> sub sum(a,b)
> if (numparams<2) b=1
> return a+b
> end sub

sub() is a numerical subroutine (because sub() doesn't contain a $-sign) and returns just the sum of its two numerical arguments. Running this program prints the line "5 3 1" which are the three values returned by the three calls to sum().

As you see the function sum() can be called with less than two arguments; in this case 0.0 is inserted for a missing numerical argument and the empty string "" for a missing string argument. However you may query local variable numparams, which is automatically created by yabasic, to get the nuber of parameters actially supplied during the subroutine call. If you omit an array argument, yabasic will supply a local dummy array instead; however, as soon as you try to access this array argument you will receive an error. Therefore it is good practice to check numparams before accessing array parameters.

Furthermore you may call a subroutine (in the example: sub(2,3)) without caring for the value returned (techincally spoken there is no distinction between functions and procedures in yabasic).

Local variables are invisible outside your subroutine, but they are newly created each time you call your subroutine; but sometimes you may want to keep the values of variables within your routines. In this case you may use static variables like this:

>     for a=1 to 4:stars():next a

>     sub stars()
>       static a$
>       local b$
>       a$=a$+"*"
>       b$=b$+"*"
>       print a$," ",b$
>     end sub

Running this program produces the following pattern:

>     * *
>     ** *
>     *** *
>     **** *

As you see the static variable a$ keeps its value over calls to stars(), while b$ is initialized every time.

Finally note, that subroutines may easily use arrays in subroutines as parameters, local or static variables; look here for an example.

Back to table of contents ...

## Defining subroutines at runtime

Sometimes a program might want to create a subroutine during its execution: Let's say a user wants to enter an arbitrary function, which the program should then plot on the screen:

    input "Please enter an arithmetic expression: f(x) = " f$

    compile "sub f(x):return "+f$+":end sub"
    for x=1 to 10:for i=1 to f(x):print "*";:next i:print:next x

This program reads an arithmetic expression into the variable f$; possible values would be "sin(x)" or "x*x". With this arithmetic expression a simple function definition is created and handed over to the compile-command, which turns its string argument into valid yabasic-code. After this you can simply use the newly defined function f(x), as in the third line, which does a simple plot of the function.

A different feature (but still related with subroutines) is the ability to execute a function by specifying its name and arguments: execute("f",x) is exactly the same as f(x); of course execute("f",x) is ugly, but it might be used to plot a function which is specified by its name only:

    sub plot(f$)

    local x,y,a

    for x=1 to 10

    y=execute(f$,x)

    for i=1 to y:print "*";:next i
    print

    next x

    end sub

With this handy plot function you may just say plot("f") to get a plot of function f. However, note that plot("x*x") would give you an error, because it would try to execute a function named "x*x" which does not exist.

Whether you want to save the value returned by execute (or whether the executed function returns one), you may use execute within an assignment or standalone: execute("f",x) and y=execute("f",x) are both valid.

Finally, you may use execute$ for functions returning a string.

Back to table of contents ...

## Libraries

A library is a separate file which contains "readymade" subroutines. Libraries are useful, if you have written a set of subroutines, which you want to use in more than one yabasic program. Better even, if someone else has written a library, which you could use without efford.

Let's go ahead and try an example (as libraries contain mostly subroutines, you may branch off and read about them first). This simply uses a library:

>     import ufirst

>     print uf$("foO")

If you run this program, it prints "Foo" which is "foO" with the first character ("f") capitalized and the rest ("oO") changed to lowercase. The name of this subroutine ("uf") is just an abbreviation of "upper-first".

The Subroutine uf$() is defined in the library ufirst which is made available with the statement import ufirst. The library ucfirst is quite simple:

>     rem Here comes the embedded documentation:
>     docu
>     docu   This library ufirst provides just a single function: uf$(),
>     doc   which returns its string argument all lower case except for
>     doc   the first letter, which is capitalized.
>     doc
>     docu   e.g. uf$("foo") returns "Foo" and uf$("bAr") returns "Bar"
>     docu

>     a=2: rem  This statement has no use !

>     export sub uf$(a$) : rem export uf$()
>     return upper$(left$(a$,1))+lower$(butfirst$(a$))
>     end sub

>     sub butfirst$(a$) : rem butfirst$() is only visible locally
>     return right$(a$,len(a$)-1)
>     end sub

If you import this library (with import ufirst) into your yabasic program, yabasic reads in the library and inserts it into your program: Any statements in the library (e.g. a=2 in the example) is executed and any subroutine (uf$() and butfirst$()) is defined. After import the variable a and the functions uf$() and butfirst$() are defined within your program. To avoid conflicts between the variables and subroutines defined in you program and those defined in the library, all variables and subroutines are prefixed with the name of the library. I.e. a is imported as ufirst.a, uf$() , is imported as ufirst.uf$() and butfirst$() is imported as ufirst.butfirst$(). You may check, that a from the library ufirst does not conflict with any a in your program with this simple program:

>     import ufirst

>     a=1
>     print a,ufirst.a

This simply prints "1 2", prooving that "a" in your program and "a" in the library ufirst don't collide.

On the other hand you may want to use some selected subroutines from the library without prefixing them with the library name. This can be achieved by adding the keyword export to the definition of the routine (in the example: export sub uf$(a$)). Such an exported subroutine can be used without prefixing it with the library name (as in print uf$("foO")).

The library starts with a bunch of docu (or doc) statements, which contain the embedded documentation of the library. This embedded documentation can be viewed by calling yabasic with the "-lib" option; you would use yabasic -lib ufirst to view the text from the docu statements. Therefore it is always a brilliant idea to add some documentation to your library, telling what subroutines the library provides. Furthermore you might put a small, embedded sample program into your library, demonstrating its usage. If you enclose this sample program with if (peek$("library"="main")) then ... endif , your embedded sample program is only executed, if the library is run as a program and not, if other programs import your library.
Once you have written some embedded documentation, you may access it also from within your program. Just query the array docu$(), which automatically contains the text of all docu-statements.

Once you have written or received a library, you should install it. This is simple: The file containing the library should end on ".yab" (e.g. ucfirst.yab); this file should than be moved to a special directory. This special directory (e.g. c:\yabasic\lib under Windows or \usr\lib\yabasic under Unix) appears (along with the explanation of the -lib option) if you call yabasic with the option -help (i.e. yabasic -help). Once you have moved the file to the right directory, you're done ! From now on you can use the library in your yabasic programs, e.g. by writing import ucfirst. Furthermore, yabasic finds a library even if it resides in the current directory, i.e. the directory where the yabasic program (with the import-statement) lives; this is quite handy for developing and testing a library.

You may notice, that the name of the file and the name of the library (which appears in the import-statement) are always (and by construction) the same.

Having read all this stuff about libraries, you may wonder where to get libraries. One solution would be to write your own; but it would be much more preferable (because it is less work) to get and use libraries other people have written. Libraries are a new feature in yabasic, so there is no set of standard libraries yet, but I hope that users will develop and contribute useful libraries ! Go ahead !!

Back to table of contents ...

## Graphics and printing

Yabasic provides some simple, general purpose graphic-commands:

  open window 400,400

      line 0,0 to 400,400
      circle 200,200,150
      clear fill circle 200,200,130
      clear line 0,400 to 400,0
      dot 200,200
      a$=inkey$
      clear window
      text 100,200,"Hello !"
      print "Press any key to close the window"
      inkey$
      close window

Drawing
  If you run this program, you will see a window with size of 400 pixels in x- and y-direction (the window size is given along with the open window-statement). To specify a certain font for the text within this window, you may add a third argument, e.g. open window 400,400,"swiss".

  Not surprising: The line-command draws a line, the circle-command draws a circle (the arguments determine x- and y-position of the center and the radius of the circle) and the dot-command draws a single dot at the specified location.
  As appropriate you may modify these commands with the keywords clear and fill: E.g. clear line simply erases the specified line; fill circle draws a black filled circle, whereas clear fill circle erases a circle with its interior.
  After the user has pressed a key (see below) the window contents is cleared with the clear window-statement. If you have your printer open (i.e. if you have issued the open printer-command) clear window finishes the current page, sends it to the printer and starts a new one.
  The next command in the example is the text-statement, which writes its text at the specified position. Aligned with this position is the left lower corner of the text. To change the alignement, you can add as a third argument a two character string; The first one specifies the horizontal alignement and can be "l" (text is left aligned), "r" (right aligned) or "c" (centered); the second character specifies the vertical alignement and can be "t" (top of text is aligned), "b" (bottom) or "c" (center). Some valid arguments would be "ct", "rb", "lc", ... By the way: Textalignement can also be changed by poking into "textalign".
  Finally close window closes the graphics-window.
Getting a key from the keyboard
  But before the window is closed, the inkey$-statement waits, until the user presses any key (in the text console or in the grafic window) and returns this key as a string. In this example the key, which is actually pressed is not important, so you may just write inkey$ (without assignment). Some important nonprintable keys (e.g. the function or cursor keys) are returned as strings: up, down, left, right, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, esc, ins, del, home, end, scrnup, scrndown, enter, tab, backspace. If your keyboard gives other keycodes than mine, or if you press a key, which is unknown to yabasic, you will receive a rather lengthy string (e.g. key1b5b31317e).
  Normally yabasic's inkey$ waits until the user presses a key; but if you want the inkey$-function to return even if no key has been pressed, you may add a timeout (in seconds) argument. E.g. inkey$(2) returns immediately, if the user hits a key and after 2 seconds (returning an empty string) if not; note that a timeout of 0 seconds is possible, which is the normal behaviour of other basics.
  If you press a mousebutton in the grafic window, you will receive a string like "MB1d+1:0100,0200" which stands for "Mouse Button 1 has gone down with the shift key pressed at x=100, y=200"; likewise "MB2u+0:0300,0400" stands for "Mouse Button 2 has gone up with no modifier key pressed at x=300, y=400". The functions mousex(), mousey(), mousemod() and mouseb() can be used to extract the details from the string returned by inkey$:
  a$=inkey$:if (left$(a$,2)="MB") print mousex(a$),mousey(a$),mousemod(a$),mouseb(a$)
  If you omit the argument (e.g. print mousex) you get the values of the last mouse-event returned by inkey$. This means, that you can not use mousex to track the position of the mouse, because it's value is updated only by the inkey$-function. Finally mouseb returns a positive value if the button has been pressed and a negative value if the button has been released.
Printing
  Getting a hardcopy of your graphics involves two new commands:
  open window 200,200

      open printer
      circle 100,100,80
      close printer
      close window

  Everything between open printer and close printer appears on paper. If you prefer sending your hardcopy to a file, you may add a filename, e.g. open printer "foo" sends the output to the file foo. Note that the open printer statement has to appear after the window has been opened. close printer can be omitted; it is done automatically, if the window is closed.
  Besides graphics you may want to print plain text to your printer:

      open #1,printer
      print #1 "Hello World !"
      close #1

  Running this program prints the text "Hello World !" on your line-printer.

Back to table of contents ...

Some more graphics

The following program shows some further commands for drawing:

  open window 600,400

      window origin "lb"

      new curve
      for x=-3 to 3 step 0.1: rem this loop draws a sine()-function
        y=sin(x)
        line to 300+90*x,200+150*y
      next x
      rectangle 10,10 to 110,60: rem draw the legend
      text 55,35,"sine-function","cc"
      inkey$
      close window

This program just draws a simple sine function.

window origin "lb"
  In normal cases the coordinate origin (i.e. the point 0,0) of any window lies in the upper left corner; the command window origin can move this origin to any of the four corners of a window. The string argument ("lb" in the example) consists of two chars; the first one can be "l" (for left) or "r" (for right); the second char can be "t" (for top) or "b" (for bottom). This gives you a total of four variants "lb", "lt", "rb" and "rt", which correspond with the four corners of a window.
  Once the origin has been moved, it applies to any grafic operation whatsoever.
rectangle 10,10 to 590,390
  This command simply draws a rectangle, defined by any two opposite corners and clears the interior. You may modify the rectangle (rect for short) command with clear and/or fill to clear the rectangle or fill it.
new curve and line to x,y
  These functions help to plot a curve as a sequence of many lines. Each line to-command draws a line from the point specified in the previous line to-command to the point specified in the command itself ; to add more line segments, you just have to specify further line to-commands. If you want to start with a new curve (i.e. a new sequence of lines) just issue the new curve-command.

Back to table of contents ...

## Bitmaps

Yabasic allows to retrieve and change rectangualr regions of the screen with simple commands:

  open window 200,200

      for a=0 to 200 step 10
        line a,0 to a,200:line 0,a to 200,a: rem draw some pattern on the screen
      next a
      rem this is a picture of a star
      star$="24,24:00c10000e10000f10000f10008f3"
      star$=star$+"0008f30008f700fff700ffff30fff"
      star$=star$+"fffefffffefffff0ffff70eff700e"
      star$=star$+"ff000cff100eff300fff70cfff70e"
      star$=star$+"f7ef1ef1cf3e700e3e100c3000000"
      for a=50 to 150
        saved$=getbit$(a,150,a+24,150+24): rem save old content of window
        bitblit star$ to a,150,"or": rem put star at new location
        pause 0.1
        bitblit saved$ to a,150: rem restore old window content
      next a

This program moves a tiny star across the window. Yabasic stores bitmaps within ordinary strings, e.g. the star-bitmap is contained within the variable star$.
The getbit$()-function returns such a bitmap-string, if called with the coordinates of a rectangle; in the example the line saved$=getbit$(a,150,a+24,150+24) stores the contents of a 24x24 rectangle with corners (a,150) and (a+24,150+24) within the variable saved$.
Once you've got a bitmap-string you can put it on the window with the putbit-command. E.g. putbit star$ to a,150,"or" puts the bitmap contained within star$ onto the screen at position (a,150). The fourth, optional argument ("or" in the example) specifies, how to combine the pixels from the window with those from the bitmap:

Mode | Action
--- | ---
"and" | The pixel is set, if both window pixel and the bitmap pixel are set.
"or" | The pixel is set, if either the window pixel or the bitmap pixel are set
"replace" | The pixel is set, if the bitmap pixel is set. This is the default
"xor" | The pixel is set, if exactly one of window and pitmap pixel is set
"nand" | The pixel is set, if not both window pixel and bitmap pixel are set
"clear" | If the bitmap pixel is set, the corrosponding window pixel is cleared
"1" | The window pixel is set, without regarding the bitmap
"0" | The window pixel is cleared, without regarding the bitmap

If you omit the mode argument, the default "replace" applies.
Having said all this, there is a serious drawback: You can put bitmaps on the window, but you can't print them !

Back to table of contents ...

## Data and Arrays

Now and then the need arises to supply a program with initial data. The next sample-program converts numbers to strings:

  restore names

      read maxnum
      dim names$(maxnum)
      for a=1 to maxnum:read names$(a):next a
      label loop
        input "Please enter a number: " number:number=int(number)
        if (number>=1 and number<=maxnum) then
          print number,"=",names$(number)
          goto loop
        endif
      print "Sorry, can't convert ",number
      label names
      data 9,"one","two","three","four","five","six"
      data "seven","eight","nine"

If you run this program, it goes like this:

  Please enter a number: 2

      2=two
      Please enter a number: 3
      3=three
      Please enter a number: 8
      8=eight
      Please enter a number: 12
      Sorry, can't convert 12

Reading Data
  As you see this program just converts numbers to their textual representation; for this purpose, it needs to know the numbers from 1 to 9 as text. This information is stored in the data-lines at the bottom of the program: With the read-command the program gets one piece of data after the other.
  If you want to deviate from the linear ordering while reading the data-statements, you may use the restore-statement: In the example above restore names makes sure, that the next read-statement reads its data after the label names.
Arrays
  In the example above the words "one" ... "nine" are stored within a string-array names$(). You may use arrays to process large quantities of data. There are numerical arrays as well as a string-arrays, but both sorts of arrays need to be declared prior to their first use; this is necessary, because yabasic needs to know, how much memory has to be reserved for the array. The example uses dim names$(maxnum) to declare a string array, another example would be dim numbers(200) to create a numerical array with 200 elements.
  More complex tasks may even require multidimensional arrays with more than one index: dim matrix(10,10) defines a two dimensional array. Array-dimension can be up to ten, if needed.
  Arrays offer more functionality than demonstrated in this simple example, you can learn about this in the section on advanced usage of arrays.

It should be mentioned, that the functionality of the above sample-program can be achieved by using totally different language-constructs:

  label loop

        input "Please enter a number: "
        number:number=int(number)
        on number+1 gosub sorry,one,two,three,four,five,sorry
      goto loop
      label sorry:print "Sorry, can't convert ",number:end
      label one:print "1=one":return
      label two:print "2=two":return
      label three:print "3=three":return
      label four:print "4=four":return
      label five:print "5=five":return

This program produces the same output as the example above.

on gosub, on goto
  The heart of this sample is the on gosub-statement, which is followed by a list of labels (sorry,one,two,...). Depending on the value of the expression (number+1) the corresponding label in the list is chosen: E.g. if number+1 gives 3, the third label (three) is selected and a gosub to this label is performed.
  A gosub is always performed, regardless of the value of the expression. More specifically, if number+1 gives anything less or equal to 1, then the first label (sorry) is chosen; if number+1 gives anything greater or equal to the number of elements in the list (which is 7 in the example), then the last label (sorry) is chosen. Therefore the label sorry is chosen whenever the program can't convert the given number.
  Finally, note that the on-construct can be used as on goto too.
End of your program
  Another new appearance in the above sample is the end-statement, which ends your program immediately. The exit statements is quite similar in ending your program, but you may add an argument (e.g. exit(2)) which will be returned to the underlying operating system. Furthermore exit terminates you program immediately, even if a grafic window is open. The third way to end a program is the error-statement, which takes a single string argument. E.g. writing error "no good" terminates the yabasic program and prints "no good" in the style of yabasic-errors.

Back to table of contents ...

## More on Arrays

Have a look at this program

>     dim a$(3,4)
>     for a=1 to 3:for b=1 to 4
>     a$(a,b)=str$(a)+","+str$(b)
>     next b:next a

>     print_array(a$())
>     dim a$(5,6)
>     print_array(a$())

>     sub print_array(b$())
>       local x,y,a,b
>       x=arraysize(b$(),1)
>       y=arraysize(b$(),2)
>       for a=1 to x
>         for b=1 to y
>           b$(a,b)="("+b$(a,b)+")"
>           print left$(b$(a,b)+"           ",10);
>         next b
>         print
>       next a
>       print
>     end sub

    If you run this program you will see something like this:

>     (1,1)     (1,2)     (1,3)     (1,4)
>     (2,1)     (2,2)     (2,3)     (2,4)
>     (3,1)     (3,2)     (3,3)     (3,4)

>     ((1,1))   ((1,2))   ((1,3))   ((1,4))   ()        ()
>     ((2,1))   ((2,2))   ((2,3))   ((2,4))   ()        ()
>     ((3,1))   ((3,2))   ((3,3))   ((3,4))   ()        ()
>     ()        ()        ()        ()        ()        ()
>     ()        ()        ()        ()        ()        ()

First this program creates the string array a$(4,3) and assigns to each element a string, which consists of the indices of the individual elements. Then the program calls the subroutine print_array() which just prints the array.

print_array() accepts an array as a parameter; when the sub is called (e.g. print_array(a$()) ), the array is passed without any indices (i.e. just a$() ).

First thing the subroutine does, is to get the size of the passed array: arraysize(b$(),1) returns the size of the first dimension of the array b$(). Likewise arraydim(b$()) returns the dimension of the supplied array (e.g. 2 in the example).

The routine print_array() not just prints the array, but also modifies it: the statement b$(a,b)="("+b$(a,b)+")" puts braces around each element. As the print_array() is called twice, array elements are modified twice, i.e. they get enclosed in double braces. This shows that the array a$() which is passed to the subroutine is modified directly. Although the subroutine uses a different name ( b$() ) for the array, it has no local copy of the array but rather works with the array from the main program ( a$() ). This behaviour is known as "call by reference" unlike the treatment of non array parameters (simple strings and numbers) which are "called by value".

After calling the routine print_array() for the first time, the sample program does another dim for the array a$(), which simply changes the arrays size. This re-dimensioning (you could just as well write redim) keeps all the old contents and initializes any new elements with the empty string (or with 0.0 for numerical arrays). If you try to reduce the size of an array (e.g. do a dim a(10) first and then dim a(5) ), the statement is ignored.

Back to table of contents ...

## Files and more on input

To understand the examples in this section, let us assume that a file named test.dat exists in the current directory and that it contains the following three lines:

    one two three
    four five
    six seven eight nine

The next example opens that file and prints out its content:

    open 1,"test.dat","r"
    while(!eof(1))
      input #1 a$
      line input b$
      print "a$=\"",a$,"\", b$=\"",b$,"\""
    wend

Opening a file
  The first thing to do if you want to use a file is to open it: open 1,"test.dat","r" opens the file test.dat and gives it the file number 1. This file number is used to refer to the file later on (e.g. input #1). File numbers can range from #1 to some maximum value (usually more than 50), which depends on your system; the hash is traditionally required. The optional third argument ("r") of the open-statement gives the filemode; depending on whether you want to open a file for reading or writing you should choose a different mode. Filemodes are borrowed from the C-language; here are the possible choices:

  Filemode | Result
  "r" | Open file for reading, start reading at the beginning of the file. This is the default
  "w" | Open file for writing, overwrite old contents
  "a" | Append to an existing file for writing or open a new one if no file with the specified name exists

  You may also try "rb", "wb", "ab"; the character "b" stands for binary mode, which allows to open and read files with non-text contents;
  If you are done with a file, you should close it, making the file number available for another open-statement.
  There is a special variant of the open-statement, which allows to check, if the statement has been successful:
    if (not open(#1,"test.dat","r")) print "Can't open the file !"
  Finally: Note that the file number (the "1" in "#1" from the above example) can be a variable. I.e. you may use this to open and read a line from a file "foo":

      a=open("foo")
      input #a a$

Specifying Window-pathnames
  Be careful, when specifying an absolute pathname: "C:\yabasic\test.dat" is not a valid pathname, because the sequence "\t" within this string is interpreted as an escape sequence, and will be translated into the Tab-character. To avoid problems like these, you should always double your backslashes like "C:\\yabasic\\test.dat", because "\\" is an escape sequence and translated into "\".
Reading and Writings
  You can write to file just the same way as you would write to your screen; the only difference is the file number, that comes with the print-statement: print #1 "Hello" writes the string "Hello" to the file with file number #1; note that there is no comma between the file number (#1) and the text to be written ("Hello"). Reading works the same way: input #1 a$, reads the variable a$ from the file with file number #1.

Back to our sample program. If your run it, you will get the following output:

>   a$="one",b$="two three"

>       a$="four", b$="five"
>       a$="six", b$="seven eight nine"

End of File
  As you can see, the program loops until the file has been fully read; this is achieved by means of the end-of-file-function eof(1), which returns false, if there are more characters in the file, whose file number is given as an argument, and returns true if the end of the file has been reached. As a special case you may use eof(0) to test if there are characters waiting on standard input; this probably makes sense only if yabasic runs as a script.
More on input
  You may already have been wondering about how the three lines of test.dat are distributed among the variables of the input-statement; this depends on the type of input-statement actually used:
  - input reads input until it encounters a space; leading and trailing spaces are skipped.
  - line input reads input until it encounters the end of a line.
    This applies regardless of whether you read from a file (e.g. input a$) or from the terminal (e.g. input #1 a$)
  Binary files
    If you need to process binary files you have to resort to peek and poke.
  Random access to files
    Normally yabasic reads one byte after the other from your file; however you may use the functions seek() and tell() to navigate within an open file:

        open #1,"test.dat","w":print #1 "abcdefghijkl";:close #1
        open #1,"test.dat","r"
        print chr$(peek(#1))," ";
        seek #1,2:print chr$(peek(#1))," ";
        seek #1,3,"here":print chr$(peek(#1))," ";
        seek #1,-2,"end":print chr$(peek(#1))," ";
        print tell(#1)
        close #1

    The Program first creates a file test.dat , which contains the text "abcdefghijkl". Afterwards single characters are read from this file with peek(); between peeks, the current position within the file is changed through seek; finally tell() gives back the position within the file. Running this program gives you one line of output: "a c g k 11".
    seek takes two or three arguments: The file number, the new offset within the file and (optionally) the position from where the offset is counted. This position can be one of "begin" (the offset counts from the beginning of the file; this is the default), "here" (the offset counts from the current position within the file) and "end" (the offset counts from the end of the file).
    Similar to the open command you may use seek within conditions (e.g. if (seek(...)) which allows you to check the success of this statement.
  Printing plain text
    You may specify the unquoted keyword printer instead of a filename to print to your line printer; see here for an example.
  The other way around ...
    Now that you understand the sample program, you may look at it rewritten:

  > >     a=open("test.dat","r")
  > >     while(!eof(a))
  > >       input #(a) a$
  > >       print a$
  > >     wend

  >   open() is used as a function, which returns the first available file number (probably 1). With the print statement you may use this file number; note however, that yabasic demands braces after the hash (as in input #(a) a$ ).

  Back to table of contents ...

  ## Interaction with the Operating System

  ### The system()-function

  Although yabasic is by no means designed as a scripting-language, it can interact with the Operating System in a limited way:

    if (peek$("os")="unix") then

          command$="ls"
        else
          command$="dir /w"
        endif
        cont$=system$(command$)
        print "This is the contents of the current directory:"
        print cont$
        print len(cont$)," characters have been printed."

  The system$()-function is the heart of this program: It hands its argument over for execution to the shell of the underlying operating system; under Unix it is the bourne-shell sh and under Windows it is command.com, which will execute the argument of the system()-function.

  If I run this program under Windows95, I receive the following output:

    This is the contents of the current directory:

        Datentraeger in Laufwerk C: heisst WIN95
        Seriennummer des Datenträgers: 0B1D-10F8
        Verzeichnis von C:\WINDOWS\Desktop
        FLOPPY.LNK    EMACS.LNK   DRUCKER.LNK
        T.YAB         TELNET.LNK  TEST.YAB     MICROS~1.LNK  CD.LNK  PLATTE.LNK
        WATCOM~1.LNK  [YABDOK~1]  TEST.DAT     WINDOW~1.LNK  [KINO]
        12 Datei(en) 2.693 Bytes 4 Verzeichnis(se)
        199.753.728 Bytes frei
        456 characters have been printed.

  Of course, you may get something different on your system (especially if you don't have a german windows installation).

  As this yabasic-program runs under Unix, as well as under Windows, the argument of the system$()-function (command$) has to be chosen according to the operating system. To find type of operating system ("unix" or "windows"), the program employs the command peek$("os").

  Finally, there is a very similar command named system() (without a trailing $), which doesn't catch the output of the executed command, which instead goes directly to your terminal. system() returns a numerical value, which is generated by the executed command. If you don't care about this value, you can safely ignore it; e.g. system("dir") (without assignment) is just as valid as a=system("dir").

  ### date$ and time$

  To print the current date and time you may write:

    print date$," ",time$

  This gave me the following output (your output will be different of course, because the times they are changing):

    5-08-28-1998-Fri-Aug 13-51-53-0

  The date$-string has six fields: 5 is the day of the week (0-6, 0 is sunday, 6 saturday), 08 is the month (01-12), 28 is the day of the month (01-31), 1998 is the year, Fri is the name of the day and Aug is the name of the month.

  The time$-string has four fields: 13 is the hour (00-23), 51 is the minute (00-59), 53 is the second (00-59) and 0 is the time, that has elapsed since the program started.

  As most fields of date$ and time$ (except the last field within time$) are fixed length, it is easy to extract fields with the mid$-function.

  Back to table of contents ...

  ## Peek and Poke

  peek and poke are an interface to some of yabasics internals and allow to query and change yabasics states and behaviour. Unlike early homecomputers, you can't peek and poke around anywhere in memory; just a few predefined variants are allowed. An example would be:

    print peek$("infolevel")

        poke "infolevel","debug"

  Which would print the current infolevel and change it to "debug".

  From this example you see: peek and poke accept string arguments (some poke-commands except an integer argument too) and peek may return a string (in this case it appears as peek$).

  Anyway there are few peek's and poke's right now, so they may be fully enumerated:

  peek$("infolevel")
    Gives back the current infolevel.
  poke "infolevel", "error"
    Sets the infolevel to any of "debug", "note", "warning", "error" or "fatal".
  poke "dump","symbols"
    Dumps a list of all known variables (including those within subroutines).
  poke "dump","subroutines"
    Dumps the call stack, i.e. lists from which sub the current sub was called, from which sub the calling sub was called and so on.
  peek("fontheight")
    Gives back the height in pixels of the font used for graphic-text.
  peek$("os")
    Gives back the operating system (either "windows" or "unix")
  poke "textalign","cc"
    This changes the way text is aligned with respect to the point given within the text-command (see there, for further explanation).
  peek$("textalign")
    Gives back a string specifying the current mode of text alignement; among possible return values are "cb", "rc", "cc", ...
  peek("version")
    This returns the version of yabasic (e.g. 2.47).
  peek$("library")
    This returns the library which contains the peek. If called from the yabasic program itself, it will return "main".
  peek("argument")
    Returns the number of arguments given to your yabasic-program. This might be useful, if you call yabasic from the command-line only; let's say you type yabasic test.yab 1 2 3. This would start yabasic to execute the program test.yab; within test.yab you could then query peek("argument") to get the number of arguments given on the command line. In the example peek("argument") would return 3, because there are three different arguments ("1", "2" and "3") on the command line (the name of the program "test.yab" doesn't count as an argument).
    To retrieve the arguments, use peek$("argument"); every call to peek$("argument") reduces the value returned by peek("argument") by one.
  peek$("argument")
    Every call to peek$("argument") returns one of the command-line arguments handed to your yabasic program. E.g.: if you call yabasic test.yab 1 2 3, then the first call to peek$("argument") would return "1", the second call would return "2", the third "3" and any further call would return an empty string ("").
  peek("read_controls")
    Returns 1 if an input statement will return nonprintable characters (e.g. ctrl-a, ctrl-v, escape); returns 0 if nonprintable characters are ignored. The initial value is 0 (i.e. nonprintable characters will be ignored).
  poke "read_controls",x
    Depending on the value of x (0 or 1) nonprintable characters will be ignored (x=0) or returned (x=1) by any subsequent input statement.

  Yabasic offers limited support for processing binary files:

  peek(#1)
    This returns the next byte from the file #1 (#2 ... #9 are possible too of course). Note that there are no quotes around #1.
  poke #1,byte
    Writes the specified byte to file #1. byte can be any number within the range 0...255
  poke #1,string
    Writes the content of string$ to file #1.

  > These peek's and poke's for binary files should not be mixed with normal file I/O through print and input (you might get trouble with yabasic's internal buffering).
  > Additionally it is wise to open such files with a filemode containing "b"

  Back to table of contents ...

  ## Advanced printing: print at(), print colour and print using

  For interactive programs you might want to print output at specific locations. Try the next example:

    clear screen

        print at(10,5) "1 -- Setup"
        print at(10,7) "2 -- Save"
        print reverse at(10,9) "3 -- Quit"
        input at(5,12) "Your choice: " a$

  If you run this program, you will get a screen resembling the following layout (note that the third line will be displayed in reverse video):

    1 -- Setup

        2 -- Save
        3 -- Quit         This line is displayed in reverse !
        Your choice:

  Note, that you should call clear screen before doing reverse printing. Afterwards, you may also use the at()-clause in print or input-statements to move to any location (specified by the two arguments of the at()-clause) on your screen. Note that at() can be written as @() too.

  If you want colour you may try this program:

  >     rem Prepare everything
  >     clear screen
  >     dim c$(8)
  >     for a=1 to 8:read c$(a):next a

  >     rem Print in various colors
  >     for a=1 to 8:for b=1 to 8
  >     print colour(c$(a),c$(b)) " -- Hello -- ";
  >     next b:next a

  >     rem Move a rectangle over the screen
  >     a$=getscreen$(1,1,10,10)
  >     for a=1 to 100:putscreen a$ to a,a:next a

  >     rem Available colors
  >     data "black","white","red","blue","green","yellow","magenta","cyan"

  This program prints "-- Hello --" with the eight colours known to yabasic: black, white, red, blue, green, yellow, magenta and cyan. With the statement colour(f$,b$) (or color(f$,b$)) you can choose the fore- and background colors for the following print-statement.

  The second part of the sample program moves a 10x10 rectangle of characters over the screen: The getscreen$(xfrom,yfrom,xto,yto) function, converts the contents of the specified screen rectangle to a string, which can be stored away in a string variable (a$ in the example). Once you have retrieved such a screen rectangle, you may put it back to the screen at any location with the putscreen command.

  Since not all terminals have the same size (of course 80x25 is the most common size), you might want to know what are the actual dimensions of your screen; There are two predefined variables for this purpose: The width of your screen can be retrieved though special peek's: peek("screenwidth") returns the width of the screen (in characters), whereas peek("screenheight") returns the height. Both peeks have meaningful values only after the first call to clear screen.

  ### print using

  To control the way numbers are printed, use the print using statement: print 12.34 using "###.####" produces 12.3400. The format string ("###.####") consists of hashes (#) with one optional dot and it pictures the appearance of the number to print. Some examples:

  Command | Output | Remarks
  --- | --- | ---
  print "=",12.34 using "###.####","=" | = 12.3400= | The output is filled up with spaces (from the left) and zeros (from the right) as the format requires.
  print "=",12.36 using ##.#,"=" | =12.4= | Last digit of output is rounded.
  print "=",12.34 using #.#,"=" | =***= | The number can not be formatted as required.

  This way of formatting is straightforward and simple, but not very flexible; e.g. it is not possible to print numbers left-aligned or with leading zeroes. To get such effects, the print using statement allows for format strings as used by the printf()-function of the C-language. Some examples:

  Print-statement | Output produced
  --- | ---
  print "==",str$(12.123455,"%08.3f"),"==" | ==0012.123==
  print "==",str$(12.123455,"%8.2f"),"==" | == 12.12==
  print "==",str$(12.123455,"%-6.2f"),"==" | ==12.12 ==

  More about these formats can be found in any book on the C-language.

  All these formats can be used for the str$()-function too.

  Back to table of contents ...

  ## Loose Ends

  Some properties of yabasic are still left to explain; here is a sample program, that employs them:

    10 beep

        pause 1
        goto 10

  This program beeps once every second:

  - beep does the beeping (can be written as bell too) and
  - pause does the waiting (can be written as wait too), its argument is the delay in seconds

  Finally, the program employs a line number (10) to mark a specific line; this feature makes yabasic more compatible with traditional basics. Line numbers are just special types of labels; they have the following properties:

  - Line numbers can appear only at the beginning of a line.
  - Not every line needs a number and line numbers need not be consecutive.
  - Line numbers can be used with the restore-statement too.

  ### Keyboard interrupts

  A feature you might need is the ability to suppress keyboard-interrupts (i.e. pressing of Ctrl-C); normally yabasic terminates immediately, if the user presses Ctrl-C. This can be suppressed like this:

    on interrupt continue

  After processing of this statement keyboard interrupts are completely ignored. The default behaviour is restored with the command on interrupt break.

  Back to table of contents ...

  # Index

  ## Index of keywords

  > + - * / ^ ? @ : # , ;
  > A: abs() acos() and and() arraydim() arraysize() asc() asin() at() atan()
  > B: beep bell bin$() break [keyboard interrupts] break [loop control]
  > C: case chr$() circle clear circle clear rect clear screen clear window close close printer close window colour color compile continue cos() curve
  > D: date$ data dec() dim do doc docu docu$() dot
  > E: else elsif end endif end sub eof() eor() error euler execute() execute$() exit exp() export
  > F: false fi fill for frac()
  > G: getbit$() getscreen$() glob() gosub goto
  > H: hex$()
  > I: if import inkey$ input input at input # instr() int() interrupt
  > J:
  > K:
  > L: label left$() len() line line input line to local log() loop lower$() ltrim$()
  > M: map() mapx() mapy() max() mid$() min() mod() mouseb mousemod mousex mousey
  > N: new curve next not numparams
  > O: on gosub on goto open open printer open window or or() origin
  > P: pause peek peek$ pi poke print print at print using print # printer putbit putscreen
  > Q:
  > R: ran() rectangle read rem redim repeat restore return reverse right$() rinstr() rtrim$()
  > S: seek() sig() sin() static step split() sqr() sqrt() str$() sub switch system() system$()
  > T: tan() tell text then time$ to token() trim$() true
  > U: until upper$() using
  > V: val()
  > W: wait wend while window window origin
  > X: xor()
  > Y:
  > Z:

  Back to table of contents ...

  ## Index of concepts

  > Arrays
  > Binary files
  > Bitmanipulation
  > Case of Keywords and Variables
  > Command line arguments
  > Conditions in the if-statement
  > Escape Sequences within strings
  > Formatting numbers
  > Getting mouse input
  > Globbing
  > How the input-statement chops a line into pieces
  > Keyboard interrupts
  > Line numbers
  > Multiple commands in one line
  > Printing Text
  > Random access to files
  > Specifying Windows-pathnames
  > Variables

  Back to table of contents ...

  # Internals

  ## History

  Yabasic started sometime around eastern 1995; a first version was completed about one month later, still missing many features. After this quick start a long period of adding features and squashing bugs followed, which has more or less persisted until today.

  The only interruption during those peaceful days came in the summer of 1996, when I got my Windows95-machine: Porting yabasic took two weeks and writing an installation program took me a month.

  ## Flex and Bison

  You may have noticed from the previous section, that yabasic made quite a rapid start; this is mainly due to flex and bison, the prime tools, used to implement yabasic.

  Bison and flex take the grammar and produce a C-program, which implements this grammar. The only thing left to the programmer is to put flesh on this skeleton.

  This process is remarkably efficient: 17 KBytes of flex and bison instructions generate 129 KBytes of C-code, which has to be compared with the 108 KBytes of C-code which I wrote. Together these implement the functionality of yabasic. So actually most of the code has been generated by flex and bison !

  ## Execution of a program

  Although yabasic behaves mostly like an interpreter, in fact it is not. Rather it's a compiler: If you give it any basic-code for execution, the code is compiled, yielding instructions for a simple stack-machine; these instructions are then interpreted immediately, so that you will never get in touch with the stack-machine. You can find out the time needed for this process if you invoke yabasic with infolevel set to note.

  Back to table of contents ...

  # Copyright

  Yabasic may be copied only under the terms of either the Artistic License or the GNU General Public License (GPL).

  Earlier versions of yabasic have been subject to the GPL alone and it is kept for backward compatibility. Since version 2.60 you have the choice to use yabasic under the terms of the Artistic License, which gives you the right to use and distribute yabasic in a more-or-less customary fashion, plus the right to make reasonable modifications, while giving the original author some semblance of artistic control over the development of yabasic.

  Regardless which license you choose (GPL or Artistic) for yabasic, it does not apply to your own yabasic-programs. The programs you write are your own work and you may use any license you like.

  Finally some examples of what is possible under the terms of the Artistic License:

  - Put yabasic on your own webpage, CD or other medium, charge for the service of distibuting yabasic.
  - Write your own yabasic-programs, put yabasic itself into the package and sell the whole thing. "Sell" is more than charging for distribution costs and time: You may make real profit with your own programs.
  - Modify yabasic, add or remove features and capabilities, sell the modified version.

  If you are still in doubt, please check out the licenses above.

  Back to table of contents ...