Full BASIC

Full BASIC, sometimes known as Standard BASIC or ANSI BASIC, is an international standard defining a dialect of the BASIC programming language. It was developed by the American National Standards Institute (ANSI) X3.60 group in partnership with the European ECMA. It describes an advanced version of BASIC with many features including structured programming, matrix math, input/output for file handling, and many other options.

ANSI's BASIC standardization was a two-stage process. The first, carried out as Minimal BASIC starting in 1974, was an effort to clearly define and standardize the original Dartmouth BASIC language so it could be correctly implemented on different platforms. After its release in late 1977, attention turned to Full BASIC which would be based on the more powerful Structured BASIC being developed at Dartmouth College. The complexity of the system and the many additions promoted by members of the standards committee led to the effort bogging down and the first draft standard was not ready until 1986, four years late.

The standard was ratified on 26 June 1986 as ECMA-116 and January 1987 as ANSI X3.113-1987. It was completely ignored; the microcomputer revolution had occurred while the specification was being argued over, and by the early-1980s Microsoft BASIC running on tens of millions of home computers had already come and gone. Watching the process drag on, the Dartmouth participants left to produce True BASIC based on parts of the standard, but this saw little use. De facto standards like Microsoft's dominated the market and formed the basis for newer languages like Microsoft Visual Basic which incorporated similar concepts.

Minimal BASIC
The introduction of Dartmouth BASIC in 1964 combined a number of emerging concepts in the computer field, including timesharing and direct interaction with the user, known at the time as a "conversational interface". General Electric, who supplied the GE 235 mainframe computer it ran on, used a modified version of Dartmouth's system to start a service bureau which would eventually evolve into the GEnie online service. Many other companies, Tymshare and CompuServe notable among them, quickly introduced hosted BASIC services of their own, following the Dartmouth model.

In 1968, Hewlett-Packard (HP) introduced the HP 2000 series minicomputers, which offered the same features of the earlier mainframe systems in a rack-mount system that could be configured in a complete form for around $100,000. Their HP Time-Shared BASIC had a number of differences from Dartmouth, and these were soon copied by other mini vendors like Data General. One holdout was Digital Equipment Corporation (DEC), who did not introduce a BASIC of their own design until 1972. This version, BASIC-PLUS was different from either the HP or Dartmouth dialects. By the early 1970s where were three major dialects and dozens of minor variations being used in the market.

In January 1974 a new group formed under the ANSI umbrella to define a single standard BASIC. The Dartmouth team formed a core part of the group. Dartmouth was working on a greatly expanded version of BASIC known as Structured BASIC (SBASIC) which became the basis for ANSI. At the time, few other dialects supported its many new features. The group decided that a complete standard based on SBASIC would take some time to agree on, so the ANSI BASIC effort was split into two milestones. The first, Minimal BASIC, would produce a standard that included only the most basic features that would be required of any implementation. Even long-supported features from Dartmouth like matrix math would be left out. The draft standard for Minimal BASIC was released in January 1976, the final draft in July 1977, and it was ratified that December. Arthur Luehrmann, a physicist from Dartmouth College who was a proponent of BASIC and part of the ANSI group later stated:

"'X3J2's first few years were spent (in hindsight, some might say 'wasted') on standardizing what amounts to the original 1964 Dartmouth Basic... Minimal Basic was more a toy than an actual language.'"

Full BASIC
The group then turned their attention to Full BASIC. By this time the microcomputer revolution was in full flight, and millions of machines running Microsoft BASIC or a similar BASIC were entering the market. In spite of this, none of the participants were microcomputer vendors or suppliers. Instead, the participants remained mainframe vendors like IBM, Control Data and General Electric, minicomputer vendors like Digital Equipment Corporation (DEC), Data General and Wang Laboratories, and other very large companies like 3M, American Express and Kodak.

The effort immediately ran afoul of the second-system effect as every member began to add their own list of "must have" features. Some wanted the language to continue the tradition of being aimed at educational uses running on small machines and desired a simple language with only rudimentary file support and similar features. Others were mainframe users that wanted to support loadable modular programs and other expansive programming features to compete with languages like COBOL or FORTRAN while offering better string manipulation. A third group was primarily interested in business applications, especially European users where BASIC had become a primary business language, and they demanded the system include extensive file handling and decimal math that did not suffer from rounding problems.

John G. Kemeny and Thomas E. Kurtz, the original designers of BASIC and members of the ANSI group, were critical of the process. In a 1982 article, Kurtz wrote about how even seemingly small issues had turned into major controversies. He used the example of the OPTION BASE statement. When arrays were first added to BASIC, they started at index 1, such that DIM A(3) made an array with three slots, 1, 2 and 3. In some cases, an index 0 is more natural, so OPTION BASE 0 was added in later versions of the Dartmouth code so the same definition would have four slots, 0 to 3. During Minimal, there was continual debate about what the default base should be, and 0 was finally selected. Five years later, during the Full efforts, it was decided that arrays could define any lower bound using new syntax, DIM YEAR(1970 TO 1990). This eliminated the demand for OPTION BASE 0 and the decision was made to change the default to 1 again.

Initially, the X3.60 group was targeting a summer 1982 date for the first technical review copy, which would be sent to the ANSI X3 committee in the fall. During this period the proposed standard would be sent out, and comments from the public would be accepted. The final draft would be sent back to X3 in 1983 for ratification that year. This proved rather optimistic. The first draft was not released until January 1985 and the final draft in 1986 for ratification in January 1987. During this time, the standard grew so large that it was ultimately split into a core module and five optional add-ons, which included complex file handling, real-time computing support, fixed decimal math, optional editing commands and even a platform-independent graphics module.

The result was criticized during the public comment period. One reviewer noted it had grown so large that "the resulting language rivals any current programming language in complexity" and that "conforming to the entire standard would compare with the most substantial compiler projects ever attempted". It goes on to describe, for instance, how there are no less than five different ways to describe a subroutine, three to define a string's maximum length and two ways to define an array. Referring to the issue of array bounds, it is noted that the committee agreed the adopted solution was "intolerable" and made plans to fix it "later".

There is no evidence that any of the participants actually built a conforming version after the release of the standard and any mention of ongoing effort promptly disappears. From 1987, the only mentions of the standard are that it exists and that True BASIC encompassed some of its features. Additionally, with millions of micros running some variation of MS's de facto standard by this point, the new ANSI standard was seen as the non-standard solution. Much of the original success of BASIC on the micro platforms was that it allowed programs to be typed in from printed source code, but by the mid-1980s this had been replaced by shrinkwrap applications and the need for BASIC as a distribution system had faded. On the large-systems side, the original use as a teaching language was being increasingly replaced by Pascal, as the external problems BASIC aimed to address, like interactivity and online editing, were now available in most systems.

True BASIC
The standards process was so slow that even the author of Structured BASIC eventually gave up on it. Stephen Garland was asked to prepare a series of College Board tests for high school students, and wrote them in Pascal instead. This was somewhat controversial given that many computers in wide use, like the Commodore 64 and TRS-80 did not have a full implementation of Pascal. Luehrmann, was critical of the effort, suggesting a more general course that would be applicable to more students.

It became clear to the Dartmouth participants in the ANSI group that the effort had no hope of being completed in any reasonable time period. They left the effort and started a new company to bring their version of the language to market as True BASIC. True BASIC combined many of the features of the core standard but also made a number of changes of its own. Among the most notable was that line numbering was now optional. The language was not well received, with many reviews expressing the same concerns about feature bloat that had been raised about the Full BASIC standard. Jerry Pournelle derided it as "madness" and John Dvorak dismissed it as "sad" and "doomed to failure."

Program editing
Like previous versions of BASIC, Full BASIC was designed to work in a line editor environment and thus uses line numbers to indicate individual or ranges of lines to be edited or removed. Line numbers could range from 1 to 50,000, in contrast to Minimal which was 0 through 9999. This meant that valid Minimal programs using line 0 were invalid in Full. Logical lines were at least 132 characters long. Logical lines could be extended across several physical lines using the "continuation character", the ampersand. Unfortunately, the ampersand was also selected as the string concatenation operator, which complicated the parser.

Additional editing commands included RENUMBER and DELETE, which by this time were common on newer microcomputer dialects. A new concept was EXTRACT, which copied a range of lines into a new file and deleted them from the original program, allowing them to be extracted to a subprogram. These could then be invoked using the CHAIN command. CHAIN could also include an optional WITH followed by a list of parameters, in which case it was expected to return a value in a variable with the same name as the program (see "Structure", below).

Basic functionality
Many of the commonly used keywords found in Minimal or other dialects remained; PRINT, INPUT, DATA and READ for instance. There were numerous minor changes to these commands. For instance, at edit time keywords can be typed in upper or lower case, or any mixture. As was the case in the past, they were normally displayed in uppercase, while a new convention was to use snake case for multi-character variable names.

Dartmouth BASIC introduced the REM statement for in-line comments and this was universally supported in other dialects. Many dialects also added a short-form, most commonly using the single-quote, ', as seen in Microsoft BASIC. For Full, they selected the exclamation mark, ! for this role, although there appears to be no reason not to use the single quote as it is not otherwise used - strings do not allow single-quote delimiters for instance. A more controversial change was that the LET keyword was now required for all assignments in order to make the parsing simpler, whereas in every other dialect LET was optional. This included Minimal, so any Minimal code using this short-cut was incompatible with Full.

On top of the relatively small set of 23 keywords and 11 functions from Minimal, Full added dozens of its own, for a total of 176 keywords (defining 161 separate concepts), 38 mathematical functions and 14 string functions if all extensions were included. A simple list of the keywords, laid out in three columns, fills two pages in the standards document.

Structure
The major difference between Full and Minimal was the addition of block-oriented structured programming constructs. In Minimal, and most BASIC interpreters, the program logically consisted of independent lines of code and one could start execution at any point by GOTOing any line number. The only exception to this rule was the FOR...NEXT loop, where all of the lines from the FOR to NEXT were logically considered to be a single block of code. Branching into or out of a FOR block would result in odd behaviour, typically implementation dependant but generally some form of error like "NEXT WITHOUT FOR".

In Full, branching into a FOR...NEXT block is not allowed, nor is branching out without using the EXIT statement. Implementations were supposed to check for such statements and disallow them, for instance, finding cases where to code GOTOed into a loop. Checking for such code is difficult in an interpreter which normally examines the program line-by-line; checking for branches into a block from other code in the program would normally require whole-program parsing like a compiler.

In Full, several existing keywords were extended, and others added, to provide additional block structures. Notable was the multi-line IF...THEN...ELSE...END IF, which allowed multiple lines of code to run if the condition was met or failed. SELECT...CASE...CASE ELSE...END SELECT was added to make decision trees, which formerly would have been implemented using the ON...GOTO or multiple IFs to select a line to run. FOR...NEXT loops remained as they were in Minimal, but a new DO...LOOP was added with top tested DO WHILE...LOOP and bottom tested DO...LOOP UNTIL... varieties. All loops could now be safely exited using the EXIT FOR and EXIT DO commands.

On top of these changes to the block structures, Full also added keywords for defining procedures, functions and program blocks. Programs as a whole were now opened with the optional PROGRAM keyword followed by a program name, and ended, as before, with END. Routines could be constructed with SUB...END SUB and called using CALL name. Multi-line functions were created with FUNCTION...END FUNCTION and did not declare a return type as that was part of the name - string function names ended with the dollar sign. The return value was provided by setting a variable to the same name as the function, for instance, a function named "DOIT" would contain a line like. Functions could call other functions and themselves, meaning that the language was naturally recursive. Full also retained the earlier style of one-line function definitions using the DEF keyword, but removed the requirement for the function name to start with "FN". For instance, DEF AVERAGE(X,Y)=(X+Y)/2.

In previous BASICs, there was no concept of scope and all variables were global. This is not adequate for the construction of large modular programs, as one section of code may have been written using common variable names like I and might change the value of that variable. As the variable is global, it retains the modified value when it returns to the original code. A key concept of structured programming is the local variable, which holds its value separate from other variables with the same name in other locations in the composite program. As BASIC did not have the concept of scope, many programs relied on the global behaviour and used variables to pass information in and out of subroutines. To allow both concepts in the new language, Full BASIC added the EXTERNAL keyword that could be added to a function or subroutine and made any variables within in local. As BASIC programs generally placed subroutines at the end of the program's source code, the DECLARE keyword was added to provide forward declarations.

Data types and variables
Full BASIC introduced long variable names, finally breaking free of the single letter or letter-digit names of the past. It set the new limit at 31 characters. One minor downside to this change was that keywords had to have spaces between them, whereas most earlier versions allowed the spaces to be left out. This was because with single-letter names a line like can be parsed as "FORS", which can not possibly be a variable in a two-letter variety of BASIC. In Full, this would have to be typed because "FORS" is a valid variable name. As was the case in earlier BASICs, data types in Full were denoted by suffixes on the variable name. Minimal had avoided this issue by only having numeric variables, but Full included strings as well, denoted using the dollar-sign, for instance A$.

Full BASIC required decimal math for the default implementation of the floating point system. As this was not universally supported in hardware, especially on minis and micros, it also added the OPTION ARITHMETIC NATIVE which indicates that math should be carried out using the system's default floating point implementation, whatever that may be. It can be returned to BCD mode with OPTION ARITHMETIC DECIMAL. This is in addition to the fixed-point math option, if installed. Numeric and string variables otherwise worked like those in other BASICs.

A new addition was the fixed-point math extension, which allowed variables to have specified accuracy. This was turned on using the command OPTIONAL ARITHMETIC FIXED followed by an asterisk and a format specifier, for instance, OPTION ARITHMETIC FIXED*8.2 would set all numeric variables to have 8 digits of accuracy and two decimal places. Such a declaration must be placed before any mathematics code in the rest of the program. Furthermore, any single variable could be individually defined using something like DECLARE NUMERIC*8.2 A, B.

Most BASICs supported the construction of array variables using the DIM keyword, for instance, DIM A(5), B(2,2) defines two arrays, the single-dimension A and two-dimension (matrix) B. In Full BASIC, the lower bound of any array was normally 1, so in this case, the variable A has five "slots", numbered 1 though 5. Using OPTION BASE 0 above this declaration would add another slot at index 0. Full also added a new system to directly specify the lower and upper bounds using the TO keyword, for instance, DIM A(100 TO 200) which makes a one-dimensional 101-slot array. To further confuse matters, DECLARE NUMERIC could also be used to create arrays; the same dimensions as the last example could be created with DECLARE NUMERIC A(100 TO 200).

Mathematics, logic and matrices
The list of supported math operators included all of those from Minimal, +, -, *, / and ^. The new MOD function returns the remainder of an integer division. The list of logical operators was expanded, AND, OR and NOT had been removed from Minimal and now re-added, and the alternative forms of comparison operators were added, =<, => and ><.

The list of primary built-in functions remained similar to previous versions of BASIC, including common examples like SQR or ABS. Trig functions were expanded to include ASIN, ACOS, ATN, COT, CSC and SEC. The new ANGLE function returned the angle between the origin and a given X,Y point. BASIC normally calculated angles in radians, but OPTION ANGLE DEGREES would convert all parameters and outputs to degrees, and the system exposed the PI function which was used in these conversions and elsewhere.

Dartmouth BASIC had introduced matrix operations relatively early in its evolution, and these were part of Full. These overload the existing math functions, so one can multiply two arrays using or multiply the contents of an array by a scalar if the B parameter is not an array. The system also adds several array-only functions, which includes ZERo, INVert and DETerminate, among others. The addition of matrix math also requires modification of existing keywords like PRINT and INPUT, which output or input multiple elements as needed to fill the array parameter.

Matrixes may be redimensioned as part of a MAT INPUT by specifying the new bounds, like MAT INPUT A(3.3). The new dimensions must have a total number of elements equal or smaller than the original DIM, so in this example if the original definition was DIM A(2,2), the input would cause an error.

Strings
Early versions of Dartmouth BASIC did not include string variables or manipulation, the only strings in a program were constants like PRINT "HELLO, WORLD!". Version 4, of 1968, added string variables and a single method to manipulate them, CHANGE, which converted strings to and from an array containing the ASCII values of the characters. For instance, CHANGE "HELLO, WORLD!" TO A would produce an array of values in A, where A(0) was 72, the decimal ASCII value for "H". This made string manipulation fairly difficult, for instance, to extract the "HELLO" from "HELLO, WORLD!", one would have to:

Many dialects of BASIC had added their own methods of performing more useful string manipulation to avoid such complication. For Full BASIC, the committee selected a variation on the concept introduced by HP, "string slicing". This concept treats the string as an array of characters and can access them using an array-like specification known as a "slice". To extract "HELLO" from "HELLO, WORLD" in Full, one would use B$=A$(1:5). The concept is similar to that of CHANGE, but this method produces results that are themselves strings, not numeric values, and thus one can PRINT B$ to produce "HELLO". One significant difference between Full's approach and previous ones like HP is that it used a different syntax for the slicing, whereas the earlier systems used array syntax. For instance, in HP, the equivalent line is B$=A$(1,5). As this is the same syntax as array accesses, HP (and similar) generally did not allow string arrays, whereas this was allowed in Full.

This approach should be contrasted with the solution selected by DEC, the use of functions that return new strings, LEFT$, MID$ and RIGHT$. This was the solution picked up by Microsoft when they wrote their BASIC on the PDP-10. Converting between the two can be error prone, to perform the equivalent of RIGHT$(n), Full would use DEF Right$(A$, n) = A$(Len(A$)-n+l).

Input/Output
Another area of focus for Full BASIC was input/output (I/O). Minimal BASIC's only I/O was the INPUT and PRINT commands and the ability to hard-code data using the DATA statements and READ it. Almost all practical dialects added OPEN and CLOSE to create a "channel" that was then used to refer to that particular file or device.

INPUT now included an optional PROMPT, followed by a string, a colon and then the input variables, for instance INPUT PROMPT "What is your age? " : A. By this time almost all BASICs included a similar feature without the word PROMPT and using the existing print separator semicolon instead of colon, for instance in MS BASIC the same line would be INPUT "What is your age? "; A. On top of this, Full also added the new TIMEOUT and ELAPSED keywords: INPUT TIMEOUT 30, ELAPSED T, PROMPT "What is your age? ": A, which will continue execution after 30 seconds even if the user enters nothing, and will put the time it took, possibly the 30 seconds, into the variable T. For systems lacking a clock (which was not uncommon at the time), T would always return -1.

PRINTing was similarly expanded with the optional USING statement, which had already appeared on a number of implementations. The USING was normally followed by a format string using number signs, asterisks and percent signs to mark decimal places. The format string could be placed in a string variable and then referred to, or an optional separate line containing a IMAGE : could be referred to by line number. Full also added new commands to set the printing area, SET MARGIN and SET ZONEWIDTH. The current values of these various settings (and others) could be returned using ASK. For instance, SET MARGIN 10 followed by ASK MARGIN J would set J to 30.

Full supported file operations with OPEN and CLOSE and a channel number prefixed with a number sign, for instance, OPEN #3: NAME "afile". Data can then be written using INPUT and PRINT or READ and the new WRITE. Additional file handling commands included ERASE and REWRITE, and all of these commands had numerous options and modes. Much of this stemmed from the different types of physical devices that were still common in the late 1970s, magnetic tape, for instance, could only be accessed sequentially so the new standard offered options for SEQUENTIAL or STREAM, etc. The list of options and their interactions and limitations covers many pages in the standard. For instance, it is possible to PRINT to a file of DISPLAY type, but not INTERNAL type, which required WRITE.

Exception handling and debugging
Like many BASICs of the era, Full added the TRACE ON command which would print out line numbers as the program executed. It could also redirect the printing of the line numbers to another device or file using TRACE ON TO #3, where #3 was a previously opened channel. It also added the DEBUG ON and related BREAK statement, the latter of which would cause an exception if debugging had previously turned on. Debugging status was limited to a particular program, so it could be turned on in one program and off in a subprogram, for instance.

Additionally, Full added true exception handlers, based on the WHEN EXCEPTION...END WHEN block. There were two ways to use this, if the code block started with WHEN EXCEPTION IN the following code was executed as a block and any exception within it would cause it to jump to the USE section, which operates similar to an ELSE. It can also be used by defining a separate subroutine-like block using HANDLER name which would then be called by name using WHEN EXCEPTION USE name. The code could test which exception had occurred using the meta-variables EXTYPE or the EXTEXT$, neither of which needed an ASK. RETRY exited the error handler and returned to the line of the error, while CONTINUE could be used within the mainline code to ignore errors even within WHEN blocks.

Graphics
Around the same time that Full was being designed, a parallel effort was underway to define the Graphics Kernel System, or GKS. This was offered as one of the optional modules in Full. This added dozens of special keywords like LINE STYLE and WINDOW with syntax that did not match that of the other modules in the standard. For instance, the CLIP command turned on clipping to the current viewport such that items drawn outside its boundaries would not be visible; this took a string value instead of a boolean, CLIP "On". Several common image modification commands were added, SHIFT, SCALE, ROTATE and SHEAR.

As images are often built up from common elements, Full added the new PICTURE block structure, which is otherwise similar to a SUB and invoked with DRAW rather than CALL. The difference is that the output of a picture block can be modified with the modification using WITH. For instance, if one defined a PICTURE CIRCLE which produced a circle of radius one, a smaller circle could be drawn and moved to the side with DRAW CIRCLE WITH SHIFT(2) * SCALE(.4).

Real-time
Full's real-time module added the concept of "parallel sections", through the PARACT keyword. These looked like subroutines (and pictures) but had a number of additional keywords that controlled their invocation. For instance, one could define code that would respond to an EVENT and then cause it to run by issuing a MESSAGE elsewhere in the code. The messages could invoke multiple handlers using the SHARED ports concept.

The system also allowed these blocks and objects to be connected to external code that would create these messages. One could, for instance, have code that waited on a device that periodically created text output, and then the appropriate handler would automatically be called when new text was available. Because the actual data from such devices tends to be multi-part, not something simple like a string or number, the real-time library also added the ability to define STRUCTUREs that could then be read or written as an atomic unit. These were then read and written using the IN FROM and OUT TO commands, or if the data was SHARED, the otherwise similar GET FROM and PUT TO.

Although many real-time programs can be modelled as a system simply responding to external events, it is also common for the program itself to periodically post these events. For this, Full added the START command and the associated WAIT which would pause execution for a given DELAY (a number of seconds) or TIME (an explicit time-of-day) or until an EVENT was seen.

ECMA vs. ANSI
The ECMA and ANSI standards were not developed jointly but in parallel, though with overlapping committee membership. Conversely, the ISO working group for BASIC did not develop a standard of their own, instead planning to adopt either the ECMA or ANSI standard.

Independently of ANSI, the ECMA committee responsible for the BASIC standard split it in two: ECMA BASIC-1 and ECMA BASIC-2. BASIC-1 included some of the file manipulation system but lacked the exception handling, while BASIC-2 added the full suite of file commands, fixed decimal math and the exception handling system. Additionally, in BASIC-1 all fundamental keywords like PRINT were reserved words which simplified the parser, while in BASIC-2 they followed the ANSI pattern and could be used within user-created subroutines and functions.

The ISO working group had initially planned to use ECMA's standard. Faced with the problem of two different candidate standards, in September 1987 it was directed to develop a single international standard unifying the two. This was accomplished by specifying that compliance to either standard could be claimed as comformance to the ISO standard.