Skip to content

N3BCKN/asbasic

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AlexScript BASIC Interpreter

An Altair-style BASIC interpreter written in AlexScript. Inspired by the Ruby BASIC tutorial, ported to AlexScript language and extended into a more modular shape.

You write line-numbered BASIC programs at the prompt, type RUN, and the interpreter executes them top to bottom (or wherever GOTO and GOSUB send you). Programs can be saved to disk, loaded back, edited line by line — the usual Altair vibe.

Files

basic.as        entry point — start the REPL
repl.as         interactive shell, command dispatcher, SAVE/LOAD
interpreter.as  statement execution (every BASIC statement lives here)
parser.as       expression parser, built-in functions, FN call dispatch
lexer.as        tokenizer
runtime.as      shared interpreter state (program buffer, variables,
                arrays, FOR/GOSUB stacks, DATA pool, flags)

The Runtime class holds all mutable state — there are no globals floating around between modules. Every other component takes a Runtime instance and operates on it.

Running

From the directory containing the files:

alexscript basic.as

You should see:

AlexScript Altair Basic 0.0.1
Type 'HELP' for available commands
>

REPL Commands

Command What it does
RUN execute the stored program
LIST display the stored program
NEW clear the stored program
CLEAR clear the screen
SAVE "file" save program to a text file
LOAD "file" load program from a text file
HELP show the command list
QUIT exit

A line that starts with a number is stored in the program buffer. Typing just the number deletes that line. Anything else is treated as an immediate command and executed on the spot.

Statements

PRINT

Comma-separated values get a space between them. A trailing semicolon suppresses the newline. Strings can be mixed with expressions freely.

PRINT "Hello, world!"
PRINT "X =", X, "Y =", Y
PRINT "no newline";

TAB(n) inside a PRINT jumps to column n.

LET (and implicit assignment)

LET X = 42
LET NAME$ = "Alice"
X = X + 1            ' LET is optional once X is defined

Array elements work the same way:

LET A(3) = 99
A(I, J) = I * J

INPUT

Prompt is optional. Multiple variables can be read from one line of input by separating them with commas.

INPUT N
INPUT "Enter your name: "; NAME$
INPUT "Coordinates: "; X, Y

$-suffixed names get the raw string, everything else is parsed as a number (with 0 as fallback if parsing fails). Array targets are supported too: INPUT A(I).

GOTO and IF / THEN / ELSE

IF X > 10 THEN PRINT "big" ELSE PRINT "small"
IF READY = 1 THEN GOTO 200
GOTO 50

The target of GOTO can be any expression that evaluates to a line number, not just a literal.

FOR / NEXT

Counter variable, end value, and optional STEP. Works for nested loops — NEXT without a variable closes the innermost loop.

FOR I = 1 TO 10
  PRINT I
NEXT I

FOR X = 10 TO 0 STEP -2
  PRINT X
NEXT X

GOSUB / RETURN / END

GOSUB pushes the next line onto a return stack and jumps; RETURN pops it. END terminates the program.

10 GOSUB 100
20 PRINT "back"
30 END
100 PRINT "subroutine"
110 RETURN

DIM

Declares an array. Indices are 0-based and inclusive — DIM A(10) gives you 11 slots from A(0) to A(10). Multi-dimensional arrays are supported.

DIM A(10)
DIM GRID(5, 5)

DEF FN

User-defined one-liner functions. Parameters shadow same-named globals during the call and are restored afterwards. Functions can call other functions in their body.

DEF FN SQUARE(X) = X * X
DEF FN HYP(A, B) = SQR(FN SQUARE(A) + FN SQUARE(B))
PRINT FN HYP(3, 4)        ' prints 5

DATA / READ / RESTORE

DATA declarations are collected into one big pool during a pre-pass before the program runs, regardless of where they appear in the source. READ consumes from the pool sequentially; RESTORE rewinds the pointer back to the start.

10 DATA "Alice", 30, "Bob", 25
20 READ NAME$, AGE
30 PRINT NAME$, AGE
40 READ NAME$, AGE
50 PRINT NAME$, AGE
60 RESTORE
70 READ NAME$
80 PRINT NAME$        ' Alice again

REM and :

REM is a comment — works on its own line or as a trailing annotation. Multiple statements can share a line by separating them with colons.

10 REM this is a header comment
20 LET X = 5            : REM initialise
30 PRINT X : PRINT X*2  : PRINT X*3

Expressions

Operator Precedence (highest to lowest)

  1. parentheses, unary -, NOT, ~
  2. ^ (exponent, right-associative)
  3. * /
  4. + -
  5. = <> < > <= >=
  6. &, AND
  7. |, OR

Note: &/|/AND/OR are treated uniformly as logical operators, not bitwise. This is a deliberate simplification from the Ruby original.

Built-in Functions

Math: ABS(x), SGN(x), SQR(x), INT(x), LOG(x), EXP(x), SIN(x), COS(x), TAN(x), ATN(x), RND (random float in [0,1))

Strings: LEN(s), STR$(n), CHR$(code), VAL(s), LEFT$(s, n), RIGHT$(s, n), MID$(s, start, length) — note MID$ is 1-indexed the way classic BASIC does it.

Identifiers

Uppercase letters followed by uppercase letters or digits, with an optional type suffix $, %, #, or !. Examples: X, A1, NAME$, COUNT%. The suffix is part of the name, so A and A$ are different variables.

Example Programs

Factorial

10 PRINT "Calculate factorial"
20 INPUT "Enter a number: "; N
30 LET FACT = 1
40 FOR I = 1 TO N
50   LET FACT = FACT * I
60 NEXT I
70 PRINT N; "! ="; FACT
> RUN
Calculate factorial
Enter a number: ? 5
5! = 120

Subroutines

10 LET N = 5
20 GOSUB 100
30 PRINT N; "! ="; F
40 LET N = 7
50 GOSUB 100
60 PRINT N; "! ="; F
70 END
100 LET F = 1
110 FOR I = 1 TO N
120   LET F = F * I
130 NEXT I
140 RETURN
> RUN
5! = 120
7! = 5040

Data and Loops with an Array

10 DATA "Alice", 30, 165
20 DATA "Bob", 25, 175
30 DATA "Carol", 35, 160
40 LET TOTALAGE = 0
50 FOR I = 1 TO 3
60   READ NAME$, AGE, HEIGHT
70   PRINT "NAME:"; NAME$; " AGE:"; AGE; " HEIGHT:"; HEIGHT
80   LET TOTALAGE = TOTALAGE + AGE
90 NEXT I
100 PRINT "Average age ="; TOTALAGE / 3
> RUN
NAME: Alice  AGE: 30  HEIGHT: 165
NAME: Bob    AGE: 25  HEIGHT: 175
NAME: Carol  AGE: 35  HEIGHT: 160
Average age = 30

User-Defined Functions

10 DEF FN SQUARE(X) = X * X
20 DEF FN CUBE(X) = X * X * X
30 FOR X = 1 TO 5
40   PRINT "x="; X; " sq="; FN SQUARE(X); " cube="; FN CUBE(X)
50 NEXT X
> RUN
x=1 sq=1 cube=1
x=2 sq=4 cube=8
x=3 sq=9 cube=27
x=4 sq=16 cube=64
x=5 sq=25 cube=125

Architecture Notes

  • Runtime class instead of globals. A single instance is threaded through the interpreter and parser. Easier to reason about, no shared mutable state across files.
  • Sorted line index. A parallel sorted array of integer line numbers lives next to the program buffer, so RUN and GOTO don't pay for a re-sort on every iteration.
  • Index-based lexer. The original Ruby version mutates a character array via shift. This port keeps a position cursor on the source string — same semantics, no per-token allocation.
  • assign_to_target shared by LET and INPUT. Whether the target is a scalar, an array element, or a multi-dim slot, both statements go through one assignment routine. Bug fixes on either side benefit both.
  • Two-pass program execution. First pass scans for DATA declarations and builds the data pool; second pass runs the program normally. DATA lines are no-ops during normal flow.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors