Sign up to the jQuery Grid Subscription list.

Monday, June 30, 2008

Reading a file with Qbasic

I'll pass quickly because I don't like to write long. First to do the remline.bas example (I even didn't try it but I want to create the code myself, without seeing the correction, some tries :) )
The following code will open a file 'file.f' and read the first line
PRINT "The file first line"
OPEN "file.f" FOR INPUT AS #1
INPUT #1, ln$
PRINT ln$
CLOSE #1
This will also show the first line, but what about the other lines.
A solution: You can think of this, looping through the lines of the file like that
OPEN "file.f" FOR INPUT AS #1
FOR i% = 1 TO 2
INPUT #1, ln$
PRINT ln$
NEXT i%
However using this solution you need to know the number of lines on the file, or an error will occur, or if you put an arbitrary number you may don't read all the lines. So we need a loop that stops when the file reach the end.
Luckily there's a function that will solve this problem!
EOF(file number) will
OPEN "file.f" FOR INPUT AS #1
DO
INPUT #1, ln$
PRINT ln$
LOOP UNTIL EOF(1)
CLOSE #1
EOF return a boolean value (0 is false and -1 is true), if eof(1) = true then the loop stops

Sunday, June 29, 2008

Analyzing the remline.bas sample

I told you the last time that to remember a language syntax or to learn a language syntax that is like another one that you know, you'll need a sample code. The sample code will give you a fast and quick startup to begin programming with that language.

Now we have to analyze the remline.bas sample in order to understand how it works and how to make cool application with Qbasic, to make powerful application in the future and who know.

Reading the comment: The first thing to start with is to read the header comments, that is the principal thing. Why fetching a code that has no advantages for us? So reading the comment will give us a general summary about this code.

Remline.bas headers' comment

'
' Microsoft RemLine - Line Number Removal Utility
' Copyright (C) Microsoft Corporation 1985-1990
'
' REMLINE.BAS is a program to remove line numbers from Microsoft Basic
' Programs. It removes only those line numbers that are not the object
' of one of the following statements: GOSUB, RETURN, GOTO, THEN, ELSE,
' RESUME, RESTORE, or RUN.
'
' When REMLINE is run, it will ask for the name of the file to be
' processed and the name of the file or device to receive the
' reformatted output. If no extension is given, .BAS is assumed (except
' for output devices). If filenames are not given, REMLINE prompts for
' file names. If both filenames are the same, REMLINE saves the original
' file with the extension .BAK.
'
' REMLINE makes several assumptions about the program:
'
' 1. It must be correct syntactically, and must run in BASICA or
' GW-BASIC interpreter.
' 2. There is a 400 line limit. To process larger files, change
' MaxLines constant.
' 3. The first number encountered on a line is considered a line
' number; thus some continuation lines (in a compiler-specific
' construction) may not be handled correctly.
' 4. REMLINE can handle simple statements that test the ERL function
' using relational operators such as =, <, and >. For example,
' the following statement is handled correctly:
'
' IF ERL = 100 THEN END
'
' Line 100 is not removed from the source code. However, more
' complex expressions that contain the +, -, AND, OR, XOR, EQV,
' MOD, or IMP operators may not be handled correctly. For example,
' in the following statement REMLINE does not recognize line 105
' as a referenced line number and removes it from the source code:
'
' IF ERL + 5 = 105 THEN END
'
' If you do not like the way REMLINE formats its output, you can modify
' the output lines in SUB GenOutFile. An example is shown in comments
What Remline.bas sample?
If you read the comment as you should, they you must understand that remline aim is fetch and find numbered lines and to delete those lines. However, it shouldn't delete necessary numbers, like the ones that a goto use!
The matter seems complicated but we need to think it a little bit, I'll start with you from 0!

The remline.bas sample on Quick Basic

I tried now to write again on Qbasic for the Google Jam Code contest. But I failed definitely! I clearly see both sides of the coin: Being a good programmer don't mean to program on any language on any time, but to have the ability to program. And the two things are different. A shark coder, don't mean he knows and master all language, plus it's easy to forget a language, you have just to keep far from it a while and you'll get your brain empty. That's normal.

That why we made samples, to face those special cases. This problem will be faced with a general sample that contain all needed functions and procedure and shows how to work with them.
I remember an old sample, very popular known as remline.bas that should refresh my memory

Note: I update the qbasic.zip file on my server, you can now find the remline.bas file with the Qbasic compiler.
Don't want to download, here's the sample code on your browser

'
' Microsoft RemLine - Line Number Removal Utility
' Copyright (C) Microsoft Corporation 1985-1990
'
' REMLINE.BAS is a program to remove line numbers from Microsoft Basic
' Programs. It removes only those line numbers that are not the object
' of one of the following statements: GOSUB, RETURN, GOTO, THEN, ELSE,
' RESUME, RESTORE, or RUN.
'
' When REMLINE is run, it will ask for the name of the file to be
' processed and the name of the file or device to receive the
' reformatted output. If no extension is given, .BAS is assumed (except
' for output devices). If filenames are not given, REMLINE prompts for
' file names. If both filenames are the same, REMLINE saves the original
' file with the extension .BAK.
'
' REMLINE makes several assumptions about the program:
'
' 1. It must be correct syntactically, and must run in BASICA or
' GW-BASIC interpreter.
' 2. There is a 400 line limit. To process larger files, change
' MaxLines constant.
' 3. The first number encountered on a line is considered a line
' number; thus some continuation lines (in a compiler-specific
' construction) may not be handled correctly.
' 4. REMLINE can handle simple statements that test the ERL function
' using relational operators such as =, <, and >. For example,
' the following statement is handled correctly:
'
' IF ERL = 100 THEN END
'
' Line 100 is not removed from the source code. However, more
' complex expressions that contain the +, -, AND, OR, XOR, EQV,
' MOD, or IMP operators may not be handled correctly. For example,
' in the following statement REMLINE does not recognize line 105
' as a referenced line number and removes it from the source code:
'
' IF ERL + 5 = 105 THEN END
'
' If you do not like the way REMLINE formats its output, you can modify
' the output lines in SUB GenOutFile. An example is shown in comments.
DEFINT A-Z

' Function and Subprocedure declarations
DECLARE FUNCTION GetToken$ (Search$, Delim$)
DECLARE FUNCTION StrSpn% (InString$, Separator$)
DECLARE FUNCTION StrBrk% (InString$, Separator$)
DECLARE FUNCTION IsDigit% (Char$)
DECLARE SUB GetFileNames ()
DECLARE SUB BuildTable ()
DECLARE SUB GenOutFile ()
DECLARE SUB InitKeyTable ()

' Global and constant data
CONST TRUE = -1
CONST false = 0
CONST MaxLines = 400

DIM SHARED LineTable!(MaxLines)
DIM SHARED LineCount
DIM SHARED Seps$, InputFile$, OutputFile$, TmpFile$

' Keyword search data
CONST KeyWordCount = 9
DIM SHARED KeyWordTable$(KeyWordCount)

KeyData:
DATA THEN, ELSE, GOSUB, GOTO, RESUME, RETURN, RESTORE, RUN, ERL, ""

' Start of module-level program code
Seps$ = " ,:=<>()" + CHR$(9)
InitKeyTable
GetFileNames
ON ERROR GOTO FileErr1
OPEN InputFile$ FOR INPUT AS 1
ON ERROR GOTO 0
COLOR 7: PRINT "Working"; : COLOR 23: PRINT " . . .": COLOR 7: PRINT
BuildTable
CLOSE #1
OPEN InputFile$ FOR INPUT AS 1
ON ERROR GOTO FileErr2
OPEN OutputFile$ FOR OUTPUT AS 2
ON ERROR GOTO 0
GenOutFile
CLOSE #1, #2
IF OutputFile$ <> "CON" THEN CLS

END

FileErr1:
CLS
PRINT " Invalid file name": PRINT
INPUT " New input file name (ENTER to terminate): ", InputFile$
IF InputFile$ = "" THEN END
FileErr2:
INPUT " Output file name (ENTER to print to screen) :", OutputFile$
PRINT
IF (OutputFile$ = "") THEN OutputFile$ = "CON"
IF TmpFile$ = "" THEN
RESUME
ELSE
TmpFile$ = ""
RESUME NEXT
END IF

'
' BuildTable:
' Examines the entire text file looking for line numbers that are
' the object of GOTO, GOSUB, etc. As each is found, it is entered
' into a table of line numbers. The table is used during a second
' pass (see GenOutFile), when all line numbers not in the list
' are removed.
' Input:
' Uses globals KeyWordTable$, KeyWordCount, and Seps$
' Output:
' Modifies LineTable! and LineCount
'
SUB BuildTable STATIC

DO WHILE NOT EOF(1)
' Get line and first token
LINE INPUT #1, InLin$
Token$ = GetToken$(InLin$, Seps$)
DO WHILE (Token$ <> "")
FOR KeyIndex = 1 TO KeyWordCount
' See if token is keyword
IF (KeyWordTable$(KeyIndex) = UCASE$(Token$)) THEN
' Get possible line number after keyword
Token$ = GetToken$("", Seps$)
' Check each token to see if it is a line number
' (the LOOP is necessary for the multiple numbers
' of ON GOSUB or ON GOTO). A non-numeric token will
' terminate search.
DO WHILE (IsDigit(LEFT$(Token$, 1)))
LineCount = LineCount + 1
LineTable!(LineCount) = VAL(Token$)
Token$ = GetToken$("", Seps$)
IF Token$ <> "" THEN KeyIndex = 0
LOOP
END IF
NEXT KeyIndex
' Get next token
Token$ = GetToken$("", Seps$)
LOOP
LOOP

END SUB

'
' GenOutFile:
' Generates an output file with unreferenced line numbers removed.
' Input:
' Uses globals LineTable!, LineCount, and Seps$
' Output:
' Processed file
'
SUB GenOutFile STATIC

' Speed up by eliminating comma and colon (can't separate first token)
Sep$ = " " + CHR$(9)
DO WHILE NOT EOF(1)
LINE INPUT #1, InLin$
IF (InLin$ <> "") THEN
' Get first token and process if it is a line number
Token$ = GetToken$(InLin$, Sep$)
IF IsDigit(LEFT$(Token$, 1)) THEN
LineNumber! = VAL(Token$)
FoundNumber = false
' See if line number is in table of referenced line numbers
FOR index = 1 TO LineCount
IF (LineNumber! = LineTable!(index)) THEN
FoundNumber = TRUE
END IF
NEXT index
' Modify line strings
IF (NOT FoundNumber) THEN
Token$ = SPACE$(LEN(Token$))
MID$(InLin$, StrSpn(InLin$, Sep$), LEN(Token$)) = Token$
END IF

' You can replace the previous lines with your own
' code to reformat output. For example, try these lines:

'TmpPos1 = StrSpn(InLin$, Sep$) + LEN(Token$)
'TmpPos2 = TmpPos1 + StrSpn(MID$(InLin$, TmpPos1), Sep$)
'
'IF FoundNumber THEN
' InLin$ = LEFT$(InLin$, TmpPos1 - 1) + CHR$(9) + MID$(InLin$, TmpPos2)
'ELSE
' InLin$ = CHR$(9) + MID$(InLin$, TmpPos2)
'END IF

END IF
END IF
' Print line to file or console (PRINT is faster than console device)
IF OutputFile$ = "CON" THEN
PRINT InLin$
ELSE
PRINT #2, InLin$
END IF
LOOP

END SUB

'
' GetFileNames:
' Gets a file name by prompting the user.
' Input:
' User input
' Output:
' Defines InputFiles$ and OutputFiles$
'
SUB GetFileNames STATIC

CLS
PRINT " Microsoft RemLine: Line Number Removal Utility"
PRINT " (.BAS assumed if no extension given)"
PRINT
INPUT " Input file name (ENTER to terminate): ", InputFile$
IF InputFile$ = "" THEN END
INPUT " Output file name (ENTER to print to screen): ", OutputFile$
PRINT
IF (OutputFile$ = "") THEN OutputFile$ = "CON"

IF INSTR(InputFile$, ".") = 0 THEN
InputFile$ = InputFile$ + ".BAS"
END IF

IF INSTR(OutputFile$, ".") = 0 THEN
SELECT CASE OutputFile$
CASE "CON", "SCRN", "PRN", "COM1", "COM2", "LPT1", "LPT2", "LPT3"
EXIT SUB
CASE ELSE
OutputFile$ = OutputFile$ + ".BAS"
END SELECT
END IF

DO WHILE InputFile$ = OutputFile$
TmpFile$ = LEFT$(InputFile$, INSTR(InputFile$, ".")) + "BAK"
ON ERROR GOTO FileErr1
NAME InputFile$ AS TmpFile$
ON ERROR GOTO 0
IF TmpFile$ <> "" THEN InputFile$ = TmpFile$
LOOP

END SUB

'
' GetToken$:
' Extracts tokens from a string. A token is a word that is surrounded
' by separators, such as spaces or commas. Tokens are extracted and
' analyzed when parsing sentences or commands. To use the GetToken$
' function, pass the string to be parsed on the first call, then pass
' a null string on subsequent calls until the function returns a null
' to indicate that the entire string has been parsed.
' Input:
' Search$ = string to search
' Delim$ = String of separators
' Output:
' GetToken$ = next token
'
FUNCTION GetToken$ (Search$, Delim$) STATIC

' Note that SaveStr$ and BegPos must be static from call to call
' (other variables are only static for efficiency).
' If first call, make a copy of the string
IF (Search$ <> "") THEN
BegPos = 1
SaveStr$ = Search$
END IF

' Find the start of the next token
NewPos = StrSpn(MID$(SaveStr$, BegPos, LEN(SaveStr$)), Delim$)
IF NewPos THEN
' Set position to start of token
BegPos = NewPos + BegPos - 1
ELSE
' If no new token, quit and return null
GetToken$ = ""
EXIT FUNCTION
END IF

' Find end of token
NewPos = StrBrk(MID$(SaveStr$, BegPos, LEN(SaveStr$)), Delim$)
IF NewPos THEN
' Set position to end of token
NewPos = BegPos + NewPos - 1
ELSE
' If no end of token, return set to end a value
NewPos = LEN(SaveStr$) + 1
END IF
' Cut token out of search string
GetToken$ = MID$(SaveStr$, BegPos, NewPos - BegPos)
' Set new starting position
BegPos = NewPos

END FUNCTION

'
' InitKeyTable:
' Initializes a keyword table. Keywords must be recognized so that
' line numbers can be distinguished from numeric constants.
' Input:
' Uses KeyData
' Output:
' Modifies global array KeyWordTable$
'
SUB InitKeyTable STATIC

RESTORE KeyData
FOR Count = 1 TO KeyWordCount
READ KeyWord$
KeyWordTable$(Count) = KeyWord$
NEXT

END SUB

'
' IsDigit:
' Returns true if character passed is a decimal digit. Since any
' Basic token starting with a digit is a number, the function only
' needs to check the first digit. Doesn't check for negative numbers,
' but that's not needed here.
' Input:
' Char$ - initial character of string to check
' Output:
' IsDigit - true if within 0 - 9
'
FUNCTION IsDigit (Char$) STATIC

IF (Char$ = "") THEN
IsDigit = false
ELSE
CharAsc = ASC(Char$)
IsDigit = (CharAsc >= ASC("0")) AND (CharAsc <= ASC("9"))
END IF

END FUNCTION

'
' StrBrk:
' Searches InString$ to find the first character from among those in
' Separator$. Returns the index of that character. This function can
' be used to find the end of a token.
' Input:
' InString$ = string to search
' Separator$ = characters to search for
' Output:
' StrBrk = index to first match in InString$ or 0 if none match
'
FUNCTION StrBrk (InString$, Separator$) STATIC

Ln = LEN(InString$)
BegPos = 1
' Look for end of token (first character that is a delimiter).
DO WHILE INSTR(Separator$, MID$(InString$, BegPos, 1)) = 0
IF BegPos > Ln THEN
StrBrk = 0
EXIT FUNCTION
ELSE
BegPos = BegPos + 1
END IF
LOOP
StrBrk = BegPos

END FUNCTION

'
' StrSpn:
' Searches InString$ to find the first character that is not one of
' those in Separator$. Returns the index of that character. This
' function can be used to find the start of a token.
' Input:
' InString$ = string to search
' Separator$ = characters to search for
' Output:
' StrSpn = index to first nonmatch in InString$ or 0 if all match
'
FUNCTION StrSpn% (InString$, Separator$) STATIC

Ln = LEN(InString$)
BegPos = 1
' Look for start of a token (character that isn't a delimiter).
DO WHILE INSTR(Separator$, MID$(InString$, BegPos, 1))
IF BegPos > Ln THEN
StrSpn = 0
EXIT FUNCTION
ELSE
BegPos = BegPos + 1
END IF
LOOP
StrSpn = BegPos

END FUNCTION

Download Quick Basic (Qbasic) 1.1

First thing to do if you want to discover Quick Basic (Qbasic) is to download it. I'm not sure if Quick Basic is the same as Qbasic, but I think there's a difference and they are two different type of compiler. Never mind, the aim now that you download Qbasic version 1.1 to get a quick look on it.

After downloading Qbasic version 1.1, you'll get a zip file, extract it and you'll find four files. Execute the exe file, and you should see Qbasic window. The help file can be accessed only from Qbasic or the command line, because it's a DOS help file.

After downloading and executing Qbasic, you can now start making code. It uses basic language, which is very easy (like you are speaking in real language).

Qbasic come back again to my desktop

How many years I have since my last use of Qbasic? 4, 5 may be 6 years! Or even more... Why did I remember this compiler and this language suddenly? What happened? I have many years programming Visual Basic .net and VB 6.0 but this small compiler left always a lasting impression.. Perhaps because it's the compiler that made me discover programming, the thing that I can't live without today.. Perhaps other factors, killing factors, killing to death...

The Qbasic is so stupid, so why I like it so!?Qbasic isn't stupid, it's an old compiler, unused nowadays and why use it? But still have many fans and I'm one of them.
It remember me of my first days as a programmer :)