{****************************************************************************** * * * TINY PASCAL BASIC * * * * 1980 S. A. MOORE * * * * Implements a small basic in Pascal. An example of how small a program can * * be to implement a simple language. * * Variables are allowed, using the letters "a" thru "z". Integers are denoted * * by the letters alone. Strings are denoted by "a$" form. * * The following statements are implemented: * * * * input Reads the contents of the variable from the user. * * If the variable is integer, a line is read from the * * user, then any spaces on the line skipped, then a * * number read. * * If the variable is string, the entire line is * * assigned to it, including any spaces. * * * * print [, Control resumes at the line specified by the integer. * * Note that no "calculated gotos" are allowed. * * * * if then The expression must be a integer. If the * * condition is 0, control resumes on the next line. * * if the condition is not 0, the statement after "then" * * is executed (as well as the rest of the line). * * * * rem The entire rest of the line is ignored. * * * * stop Terminates program execution. The values of variables * * are not cleared. * * * * run All variables are cleared, with integers becoming 0, * * and strings becoming empty. Then control passes to * * the first statement in the program. * * * * list [[,]] Lists all program lines between the given lines. * * The default if no lines are given is the starting * * and ending lines of the entire program. * * * * new Clears the entire program and stops execution. * * * * [let] = Assigns the value of the expression to the * * variable. The variable must be the same type (string * * or integer) as the expression. The "let" keyword is * * optional. * * * * bye Exits basic for the operating system. * * * * Expressions can contain the following operators: * * * * <, >, =, <>, <=, >= Comparision. * * +, -, *, /, mod Basic math. * * left$(, ) The leftmost characters of the string. * * right$(, ) The rightmost characters of the string. * * mid$(, , ) The middle characters of the string. * * str$() The string form of the integer expression. * * val() The integer equivalent of the string. * * chr() The ascii value of the first character. * * * * The internal form of the program is keyword compressed for effiency, which * * both allows for a smaller internal program, and simplifies the decoding of * * keywords. * * * * * * Notes: * * * * 1. If the program store were of the same form as basic strings, routines * * that handle both in common could be used (example: getting a number from * * the string). * * * ******************************************************************************} program basics(input, output); label 88, 77, 99; const maxlin = 9999; { maximum line number } maxpgm = 100; { maximum line store } maxstk = 10; { maximum temp count } maxkey = 29; { maximum key store } { key codes } cinput = 1; cprint = 2; cgoto = 3; cif = 4; crem = 5; cstop = 6; crun = 7; clist = 8; cnew = 9; clet = 10; cbye = 11; clequ = 12; cgequ = 13; cequ = 14; cnequ = 15; cltn = 16; cgtn = 17; cadd = 18; csub = 19; cmult = 20; cdiv = 21; cmod = 22; cleft = 23; cright = 24; cmid = 25; cthen = 26; cstr = 27; cval = 28; cchr = 29; type string10 = packed array [1..10] of char; { key } string80 = packed array [1..80] of char; { general string } bstring80 = record len : integer; str : string80 end; vartyp = (tint, tstr); { variable type } { error codes } errcod = (eitp, estate, eexmi, eeque, estyp, epbful, eiovf, evare, elabnf, einte, econv, elntl, ewtyp, erpe, eexc, emqu, eifact, elintl, estrovf, eedlexp, elpe, ecmaexp, estre, estrinx); var prgm: array [0..maxpgm] of string80; { program store } strs: array ['a'..'z'] of bstring80; { string store } ints: array ['a'..'z'] of integer; { integer store } keywd: array [cinput..cchr] of string10; { keywords } temp: array [1..maxstk] of record typ : vartyp; int : integer; bstr : bstring80 end; prgmc, { program counter (0 = input line) } top, { current temps top } linec: integer; { character position } { print key compressed line } procedure prtlin(var str : string80); var i, j: integer; procedure prtkey(var str : string10); var i, j: integer; begin { prtkey } j := 10; while (str[j] = ' ') and (j > 0) do j := j - 1; j := j + 1; i := 1; while i < j do begin write(str[i]); i := i + 1 end end; { prtkey } begin { prtlin } j := 80; while (str[j] = ' ') and (j > 0) do j := j - 1; j := j + 1; i := 1; while i < j do begin if ord(str[i]) < ord(' ') then prtkey(keywd[ord(str[i])]) else write(str[i]); i := i + 1 end; writeln end; { prtlin } { print error } procedure prterr(err : errcod); begin if prgmc <> 0 then prtlin(prgm[prgmc]); write('*** '); case err of eitp: writeln('Interpreter error'); estate: writeln('Statement expected'); eexmi: writeln('Expression must be integer'); eeque: writeln('"=" expected'); estyp: writeln('Operands not of same type'); epbful: writeln('Program buffer full'); eiovf: writeln('Input overflow'); evare: writeln('Variable expected'); elabnf: writeln('Statement label not found'); einte: writeln('Integer expected'); econv: writeln('Conversion error'); elntl: writeln('Line number too large'); ewtyp: writeln('Operand(s) of wrong type'); erpe: writeln('")" expected'); eexc: writeln('Expression too complex'); emqu: writeln('Missing quote'); eifact: writeln('Invalid factor'); elintl: writeln('Line number too large'); estrovf: writeln('String overflow'); eedlexp: writeln('End of line expected'); elpe: writeln('"(" expected'); ecmaexp: writeln('"," expected'); estre: writeln('String expected'); estrinx: writeln('String indexing error') end; goto 88 { loop to ready } end; { check character } function chkchr : char; var c: char; begin if linec <= 80 then c := prgm[prgmc][linec] else c := ' '; chkchr := c end; { check end of line } function chkend: boolean; begin chkend := linec > 80 { past end of line } end; { get character } function getchr: char; begin getchr := chkchr; if not chkend then linec := linec + 1 end; { check next character } function chknxt(c : char) : boolean; begin chknxt := c = chkchr; if c = chkchr then c := getchr end; { skip spaces } procedure skpspc; var c: char; begin while (chkchr = ' ') and not chkend do c := getchr; end; { check end of statement } function chksend: boolean; begin skpspc; { skip spaces } chksend := chkend or (chkchr = ':') { check eoln or ':' } end; { check null string } function null(var str : string80) : boolean; var i: integer; f: boolean; begin f := true; for i := 1 to 80 do if str[i] <> ' ' then f := false; null := f end; { check digit } function digit(c : char) : boolean; begin digit := (ord(c) >= ord('0')) and (ord(c) <= ord('9')) end; { convert to lower case } function lcase(c : char) : char; begin if (ord(c) >= ord('A')) and (ord(c) <= ord('Z')) then c := chr(ord(c) - ord('A') + ord('a')); lcase := c end; { check alphabetical } function alpha(c : char) : boolean; begin alpha := (ord(lcase(c)) >= ord('a')) and (ord(c) <= ord('z')) end; { parse leading integer } function lint(var str : string80) : integer; var i, v: integer; b: boolean; begin v := 0; i := 1; while (i < 80) and (str[i] = ' ') do i := i + 1; repeat if digit(str[i]) then begin v := v*10 + (ord(str[i]) - ord('0')); if i <> 80 then begin i := i + 1; b := false end else b := true end else b := true until b; lint := v end; { search label } function schlab(lab : integer):integer; var i: integer; begin i := 1; while (lab <> lint(prgm[i])) and (i <= maxpgm) do i := i + 1; if lab <> lint(prgm[i]) then prterr(elabnf); schlab := i end; { input string } procedure inpstr(var str : string80); var i: integer; begin for i := 1 to 80 do str[i] := ' '; i := 1; while (i <= 80) and not eoln do begin read(str[i]); i := i + 1 end; readln; if (i > 80) then prterr(eiovf) end; { parse variable reference } function getvar : char; begin if not alpha(chkchr) then prterr(evare); getvar := lcase(getchr) end; { enter line to store } procedure enter(var str : string80); var line, i, j, k: integer; f: boolean; begin line := lint(str); if line > maxlin then prterr(elintl); { input line number to large } i := 1; f := false; repeat if null(prgm[i]) then f := true else if lint(prgm[i]) < line then begin i := i + 1; if i > maxpgm then f := true end else f := true until f; if i > maxpgm then prterr(epbful); if null(prgm[i]) then prgm[i] := str else if lint(prgm[i]) = line then begin j := 1; while (str[j] = ' ') and (j < 80) do j := j + 1; while digit(str[j]) and (j < 80) do j := j + 1; while (str[j] = ' ') and (j < 80) do j := j + 1; if j = 80 then begin for k := i to maxpgm - 1 do prgm[k] := prgm[k + 1]; for j := 1 to 80 do prgm[maxpgm][j] := ' ' end else prgm[i] := str end else if not null(prgm[maxpgm]) then prterr(epbful) else begin for k := maxpgm downto i + 1 do prgm[k] := prgm[k - 1]; prgm[i] := str end end; { compress keys } procedure keycom(var str : string80); var ts: string80; k, i1, i2: integer; f: boolean; c: char; function matstr(var stra: string80; var i: integer; var strb: string10): boolean; var i1, i2: integer; f: boolean; begin { matstr } i1 := i; i2 := 1; repeat if strb[i2] = ' ' then f := false else if lcase(stra[i1]) = lcase(strb[i2]) then begin f := true; i1 := i1 + 1; i2 := i2 + 1 end else f := false until not f or (i1 > 80) or (i2 > 10); if i2 > 10 then begin f := true; i := i1 end else if strb[i2] = ' ' then begin f := true; i := i1 end else f := false; matstr := f end; { matstr } { } begin { keycom } for i2 := 1 to 80 do ts[i2] := ' '; i1 := 1; i2 := 1; repeat if str[i1] = '"' then begin ts[i2] := '"'; i1 := i1 + 1; i2 := i2 + 1; c := ' '; while (i1 <= 80) and (c <> '"') do begin c := str[i1]; ts[i2] := str[i1]; i1 := i1 + 1; i2 := i2 + 1 end end else if str[i1] = ' ' then begin ts[i2] := str[i1]; i1 := i1 + 1; i2 := i2 + 1 end else begin k := 1; f := false; while (k <= maxkey) and not f do begin f := matstr(str, i1, keywd[k]); k := k + 1 end; if f then ts[i2] := chr(k - 1) else begin ts[i2] := str[i1]; i1 := i1 + 1 end; i2 := i2 + 1 end until i1 > 80; for i1 := 1 to 80 do str[i1] := ts[i1] { this diagnostic prints the resulting tolken sequence } {;for i1 := 1 to 80 do write(ord(str[i1]), ' ');} end; { keycom } { get integer } function getint: integer; var v: integer; begin v := 0; skpspc; if not digit(chkchr) then prterr(einte); repeat v := v*10 + (ord(getchr) - ord('0')) until not digit(chkchr); getint := v end; { get integer from string } function getval(var str: string80): integer; var i: integer; begin i := 1; while (i <= 80) and (str[i] = ' ') do i := i + 1; if not digit(str[i]) then prterr(einte); getval := lint(str); while (i < 80) and digit(str[i]) do i := i + 1; while (i < 80) and (str[i] = ' ') do i := i + 1; if i <> 80 then prterr(econv) end; { get integer from basic string } function getbval(var str: bstring80): integer; var i, v: integer; begin i := 1; while (i <= str.len) and (str.str[i] = ' ') do i := i + 1; { skip spaces } if not digit(str.str[i]) then prterr(einte); { number not present } v := 0; { clear result } while (i <= str.len) and digit(str.str[i]) do begin { parse digit } v := v*10+ord(str.str[i])-ord('0'); { scale, convert and add in digit } i := i+1 { next character } end; while (i <= str.len) and (str.str[i] = ' ') do i := i + 1; if i <= str.len then prterr(econv); getbval := v { return result } end; { place integer to string } procedure putbval(var str: bstring80; v: integer); var p: integer; { power holder } i: integer; { string index } begin str.len := 0; { clear result string } p := 10000; { set maximum power } i := 1; { set 1st character } if v < 0 then begin { negative } str.str[i] := '-'; { place minus sign } i := i + 1; { next character } v := -v { negate number } end; while p <> 0 do begin { fit powers } str.str[i] := chr(v div p+ord('0')); { place digit } if str.str[1] = '-' then begin { negative } if (str.str[2] <> '0') or (p = 1) then i := i + 1; { next digit } end else { positive } if (str.str[1] <> '0') or (p = 1) then i := i + 1; { next digit } v := v mod p; { remove from value } p := p div 10 { find next power } end; str.len := i-1 { set length of string } end; { print basic string } procedure prtbstr(var bstr: bstring80); var i: integer; begin for i := 1 to bstr.len do write(bstr.str[i]); end; { input basic string } procedure inpbstr(var bstr: bstring80); var i: integer; begin for i := 1 to 80 do bstr.str[i] := ' '; i := 1; while (i < 80) and not eoln do begin read(bstr.str[i]); i := i + 1 end; if (i > 80) and not eoln then prterr(eiovf); readln; bstr.len := i end; { concatenate basic strings } procedure cat(var bstra, bstrb: bstring80); var i: integer; { index for string } begin if (bstra.len + bstrb.len) > 80 then prterr(estrovf); { string overflow } { copy source after destination } for i := 1 to bstrb.len do bstra.str[bstra.len+i] := bstrb.str[i]; bstra.len := bstra.len + bstrb.len { set new length } end; { check stack items equal } function chkequ : boolean; begin if (temp[top].typ <> tint) or (temp[top - 1].typ <> tint) then prterr(ewtyp); chkequ := temp[top - 1].int = temp[top].int end; { check stack items less than } function chkltn: boolean; begin if (temp[top].typ <> tint) or (temp[top - 1].typ <> tint) then prterr(ewtyp); chkltn := temp[top - 1].int < temp[top].int end; { check stack items greater than } function chkgtn: boolean; begin if (temp[top].typ <> tint) or (temp[top - 1].typ <> tint) then prterr(ewtyp); chkgtn := temp[top - 1].int > temp[top].int end; { set tos true } procedure settrue; begin temp[top].typ := tint; temp[top].int := 1 end; { set tos false } procedure setfalse; begin temp[top].typ := tint; temp[top].int := 0 end; { clear program store } procedure clear; var x, y: integer; c: char; begin for x := 1 to maxpgm do for y := 1 to 80 do prgm[x][y] := ' '; for c := 'a' to 'z' do strs[c].len := 0; for c := 'a' to 'z' do ints[c] := 0; prgmc := 0; linec := 1; top := 1 end; { clear variable store } procedure clrvar; var c: char; begin for c := 'a' to 'z' do strs[c].len := 0; for c := 'a' to 'z' do ints[c] := 0; prgmc := 0; linec := 1; top := 1 end; { execute string } procedure exec; label 1; { exit procedure } var c: char; { execute statement } procedure stat; var x, y: integer; c: char; s: string80; b: boolean; { parse expression } procedure expr; { parse simple expression } procedure sexpr; { parse term } procedure term; { parse factor } procedure factor; var i: integer; c: char; begin { factor } skpspc; c := chkchr; { save starting character } if chknxt('(') then begin expr; if not chknxt(')') then prterr(erpe) end else if chknxt(chr(cadd)) then begin factor; if temp[top].typ <> tint then prterr(ewtyp) end else if chknxt(chr(csub)) then begin factor; if temp[top].typ <> tint then prterr(ewtyp); temp[top].int := - temp[top].int end else if chknxt('"') then begin top := top + 1; if top > maxstk then prterr(eexc); temp[top].typ := tstr; i := 1; while (i <= 80) and (chkchr <> '"') do begin temp[top].bstr.str[i] := getchr; i := i + 1 end; if not chknxt('"') then prterr(emqu); temp[top].bstr.len := i - 1 end else if digit(chkchr) then begin top := top + 1; if top > maxstk then prterr(eexc); temp[top].typ := tint; temp[top].int := getint end else if alpha(chkchr) then begin top := top + 1; if top > maxstk then prterr(eexc); c := getvar; if chknxt('$') then begin temp[top].typ := tstr; temp[top].bstr := strs[c] end else begin temp[top].typ := tint; temp[top].int := ints[c] end end else if chknxt(chr(cleft)) or chknxt(chr(cright)) or chknxt(chr(cmid)) then begin { left$, right$ } skpspc; { skip spaces } if not chknxt('(') then prterr(elpe); { '(' expected } expr; { parse expression } if temp[top].typ <> tstr then prterr(estre); { string expected } skpspc; { skip spaces } if not chknxt(',') then prterr(ecmaexp); { ',' expected } expr; { parse expression } if temp[top].typ <> tint then prterr(einte); { integer expected } skpspc; { skip spaces } if c <> chr(cmid) then begin { left$ or right$ } if not chknxt(')') then prterr(erpe); { ')' expected } if temp[top].int > temp[top-1].bstr.len then prterr(estrinx); if c = chr(cright) then { right$ } for i := 1 to temp[top].int do { move string left } temp[top-1].bstr.str[i] := temp[top-1].bstr.str[i+temp[top-1].bstr.len-temp[top].int]; temp[top-1].bstr.len := temp[top].int; { set new length left } top := top-1 { clean stack } end else begin { mid$ } if not chknxt(',') then prterr(ecmaexp); { ',' expected } expr; { parse end expression } if temp[top].typ <> tint then prterr(einte); { integer expected } skpspc; { skip spaces } if not chknxt(')') then prterr(erpe); { ')' expected } { check requested length > string length } if temp[top].int+temp[top-1].int-1 > temp[top-2].bstr.len then prterr(estrinx); for i := 1 to temp[top].int do { move string left } temp[top-2].bstr.str[i] := temp[top-2].bstr.str[i+temp[top-1].int-1]; temp[top-2].bstr.len := temp[top].int; { set new length left } top := top-2 { clean stack } end end else if chknxt(chr(cchr)) then begin { chr } if not chknxt('(') then prterr(elpe); { '(' expected } expr; { parse expression } if temp[top].typ <> tstr then prterr(estre); { string expected } skpspc; { skip spaces } if not chknxt(')') then prterr(erpe); { ')' expected } if temp[top].bstr.len < 1 then prterr(estrinx); { check valid } c := temp[top].bstr.str[1]; { get the 1st character } temp[top].typ := tint; { change to integer } temp[top].int := ord(c) { place result } end else if chknxt(chr(cval)) then begin { val } if not chknxt('(') then prterr(elpe); { '(' expected } expr; { parse expression } if temp[top].typ <> tstr then prterr(estre); { string expected } skpspc; { skip spaces } if not chknxt(')') then prterr(erpe); { ')' expected } i := getbval(temp[top].bstr); { get string value } temp[top].typ := tint; { change to integer } temp[top].int := i { place result } end else if chknxt(chr(cstr)) then begin { str$ } if not chknxt('(') then prterr(elpe); { '(' expected } expr; { parse expression } if temp[top].typ <> tint then prterr(einte); { integer expected } skpspc; { skip spaces } if not chknxt(')') then prterr(erpe); { ')' expected } i := temp[top].int; { get value } temp[top].typ := tstr; { change to string } putbval(temp[top].bstr, i) { place value in ascii } end else prterr(eifact) end; { factor } begin { term } factor; skpspc; while ord(chkchr) in [cmult, cdiv, cmod] do begin case ord(getchr) of { tolken } cmult: begin { * } factor; if (temp[top].typ <> tint) or (temp[top - 1].typ <> tint) then prterr(ewtyp); temp[top - 1].int := temp[top - 1].int * temp[top].int; top := top - 1 end; cdiv: begin { / } factor; if (temp[top].typ <> tint) or (temp[top - 1].typ <> tint) then prterr(ewtyp); temp[top - 1].int := temp[top - 1].int div temp[top].int; top := top - 1 end; cmod: begin { mod } factor; if (temp[top].typ <> tint) or (temp[top - 1].typ <> tint) then prterr(ewtyp); temp[top - 1].int := temp[top - 1].int mod temp[top].int; top := top - 1 end end; skpspc { skip spaces } end end; { term } begin { sexpr } term; skpspc; while ord(chkchr) in [cadd, csub] do begin case ord(getchr) of { tolken } cadd: begin term; if temp[top].typ = tstr then begin if temp[top - 1].typ <> tstr then prterr(estyp); cat(temp[top - 1].bstr, temp[top].bstr); top := top - 1 end else begin if temp[top - 1].typ <> tint then prterr(estyp); temp[top - 1].int := temp[top - 1].int + temp[top].int; top := top - 1; end end; csub: begin { - } term; if (temp[top].typ <> tint) or (temp[top - 1].typ <> tint) then prterr(ewtyp); temp[top - 1].int := temp[top - 1].int - temp[top].int; top := top - 1 end end; skpspc { skip spaces } end end; { sexpr } begin { expr } sexpr; { parse simple expression } skpspc; { skip spaces } while ord(chkchr) in [cequ, cnequ, cltn, cgtn, clequ, cgequ] do begin case ord(getchr) of { tolken } cequ: begin sexpr; if chkequ then begin top := top - 1; settrue end else begin top := top - 1; setfalse end end; cnequ: begin sexpr; if chkequ then begin top := top - 1; setfalse end else begin top := top - 1; settrue end end; cltn: begin sexpr; if chkltn then begin top := top - 1; settrue end else begin top := top - 1; setfalse end end; cgtn: begin sexpr; if chkgtn then begin top := top - 1; settrue end else begin top := top - 1; setfalse end end; clequ: begin sexpr; if chkgtn then begin top := top - 1; setfalse end else begin top := top - 1; settrue end end; cgequ: begin sexpr; if chkltn then begin top := top - 1; setfalse end else begin top := top - 1; settrue end end end; skpspc { skip spaces } end end; { expr } { process "let" function } procedure let; begin skpspc; c := getvar; if chknxt('$') then begin skpspc; if not chknxt(chr(cequ)) then prterr(eeque); expr; if temp[top].typ <> tstr then prterr(estyp); strs[c] := temp[top].bstr; top := top - 1 end else begin skpspc; if not chknxt(chr(cequ)) then prterr(eeque); expr; if temp[top].typ <> tint then prterr(estyp); ints[c] := temp[top].int; top := top - 1 end end; begin { stat } skpspc; if ord(chkchr) < ord(' ') then begin if ord(chkchr) > cbye then prterr(estate); case ord(getchr) of { statement } cinput: begin skpspc; c := getvar; if chknxt('$') then inpbstr(strs[c]) else begin inpstr(s); ints[c] := getval(s) end end; cprint: begin repeat { list items } expr; if temp[top].typ = tstr then prtbstr(temp[top].bstr) else write(temp[top].int); top := top - 1; skpspc until not chknxt(','); { until not ',' } if not chknxt(';') then writeln end; cgoto: begin prgmc := schlab(getint); goto 1 end; cif: begin expr; if temp[top].typ <> tint then prterr(eexmi); if temp[top].int = 0 then begin top := top - 1; { go next line } if prgmc > 0 then prgmc := prgmc + 1; goto 1 end; top := top - 1; b := chknxt(chr(cthen)); stat end; crem: begin if prgmc > 0 then prgmc := prgmc + 1; { go next line } goto 1 { exit line executive } end; cstop: goto 88; crun: begin clrvar; prgmc := 1; goto 1 end; clist: begin x := 1; { set default list swath } y := maxpgm; if not chksend then begin { list swath is specified } x := schlab(getint); skpspc; { check if end line is specified } if chknxt(',') then y := schlab(getint) end; for x := x to y do { print specified lines } if not null(prgm[x]) then { line exists in buffer } prtlin(prgm[x]) { print } end; cnew: begin clear; goto 88 end; clet: let; cbye: goto 99 end end else let { default let } end; { stat } begin { exec } linec := 1; while digit(chkchr) do c := getchr; { skip label } repeat stat until getchr <> ':'; skpspc; if not chkend then prterr(eedlexp); { should be at line end } if prgmc > 0 then prgmc := prgmc + 1; 1: end; { exec } begin { executive } clear; { initalize keys } keywd[cinput] := 'input '; keywd[cprint] := 'print '; keywd[cgoto] := 'goto '; keywd[cif] := 'if '; keywd[crem] := 'rem '; keywd[cstop] := 'stop '; keywd[crun] := 'run '; keywd[clist] := 'list '; keywd[cnew] := 'new '; keywd[clet] := 'let '; keywd[cbye] := 'bye '; keywd[clequ] := '<= '; keywd[cgequ] := '>= '; keywd[cequ] := '= '; keywd[cnequ] := '<> '; keywd[cltn] := '< '; keywd[cgtn] := '> '; keywd[cadd] := '+ '; keywd[csub] := '- '; keywd[cmult] := '* '; keywd[cdiv] := '/ '; keywd[cmod] := 'mod '; keywd[cleft] := 'left$ '; keywd[cright] := 'right$ '; keywd[cmid] := 'mid$ '; keywd[cthen] := 'then '; keywd[cstr] := 'str$ '; keywd[cval] := 'val '; keywd[cchr] := 'chr '; writeln; writeln('Tiny basic interpreter vs. 0.1 Copyright (C) 1994 S. A. Moore'); writeln; 88: while true do begin writeln('Ready'); 77: prgmc := 0; linec := 1; top := 0; { get user lines until non-blank } repeat inpstr(prgm[0]) until not null(prgm[0]); keycom(prgm[0]); if lint(prgm[0]) > 0 then begin enter(prgm[0]); goto 77 end else repeat exec; if (prgmc > maxpgm) then prgmc := 0 else if null(prgm[prgmc]) then prgmc := 0 until prgmc = 0 end; 99: writeln end.