{[b+]} { NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE: Copyright 1980, 1981, 1984 by Oregon Software, Inc. All Rights Reserved. This computer program is the property of Oregon Software, Inc. of Portland, Oregon, U.S.A., and may be used and copied only as specifically permitted under written license agreement signed by Oregon Software, Inc. Whether this program is copied in whole or in part and whether this program is copied in original or in modified form, ALL COPIES OF THIS PROGRAM MUST DISPLAY THIS NOTICE OF COPYRIGHT AND OWNERSHIP IN FULL. Conversion Aid, for converting from Pascal-1 to Pascal-2 Release version: 2.0J Level: 3 Date: 24-Sep-1981 08:35:47 Processor: PDP11 } program convrs(input, output, source, result); { Processor to scan a Pascal 1.2 program and flag incompatibilities with Pascal 2 which will not be detected by the Pascal 2 compiler. The specific constructs detected are: 1. All embedded switches 2. Embedded macro code. 3. External procedure references. 4. External procedure definitions. 5. Use of the "Origin" feature. 6. Undiscriminated record variants. This is by no means a complete sample of all trouble spots, but it should catch most of the problems which the compiler won't catch. The command line syntax is: OUTPUT FILENAME = INPUT FILENAME. Default extension values are ".CVR" for output and ".PAS" for input. } procedure gmcr;external; {get RSX command line} label 99; {exit label when eof encountered} const max_line_len = 132; {max length of an input line} max_id = 10; {max length of an identifier} ht = 9; {ord of tab character} blank_id = ' '; {a blank identifier} qvar = 'var '; {reserved words} qend = 'end '; qtype = 'type '; qcase = 'case '; qbegin = 'begin '; qorigin = 'origin '; qforward = 'forward '; qfortran = 'fortran '; qextern = 'external '; qfunc = 'function '; qproc = 'procedure '; file_name_len = 60; {max size of a file name} rt11 = true;{set true only if running on RT-11 system} rsts = false;{true only if running on RSTS system} rsx = false; {set true only if running on RSX system} rsxprompt = 'CON>'; {prompt to use if on RSX system} cmdlinelength = 132; { length of a command line } type file_name = packed array [1..file_name_len] of char; symbol = (procsy, funcsy, beginsy, externsy, forwardsy, typesy, varsy, casesy, endsy, originsy, fortransy, lpar, rpar, colon, ident, nonesy); symbol_set = set of symbol; identifier = packed array [1..max_id] of char; line_buffer = packed array [1..max_line_len] of char; message = (embed_switch, embed_code, extern_ref, origin_used, undesc_variant); message_set = set of message; cmdindex = 1..cmdlinelength; {pointer to chars in command line} cmdbuffer = packed array [cmdindex] of char; {command line buffer} var this_line: line_buffer; {line being read} line_len: 0..max_line_len; {chars read on this line} line_no: 0..maxint; {line number in file} current_file: file_name; {current file name} file_len: integer; {length of current file name} more_files: boolean; {there are more input files} endline: boolean; {end of current input line} source: text; {current source file} result: text; {result listing file} ch: char; {current char} sym: symbol; {current symbol} extern_count: integer; {switch counter for external procedures} {diagnostic data accumulated} messages_this_line: message_set; {messages applying to this line} messages: message_set; {all messages ever given} file_printed: boolean; {current file name already printed} cmdline: cmdbuffer; {actual command line read} cmdlength: cmdindex; {length of line being read in} fileline: cmdbuffer; {command line containing only files} nextf: cmdindex; {next character in fileline} dev_start, dev_end: 0..cmdlinelength; {start and end of current device} uic_start, uic_end: 0..cmdlinelength; {start and end of current uic} procedure getline(var line: cmdbuffer; {resulting command line} var length: cmdindex {resulting command length} ); { RT 11 procedure to get a command line no matter what its source. } external; procedure exitst(status: integer {status for error on exit} ); { Entrance to the library to exit with a status set to "status". } external; procedure getcmdline; var i: integer; {induction var} begin {read the command into memory} if rsx then begin gmcr; if input^ <> ' ' then begin repeat get(input) until (input^ = ' '); while not eoln and (input^ = ' ') do get(input); end; if input^ = ' ' then write(rsxprompt); if eoln then readln; end else if rsts then write('*'); if rt11 then begin getline(cmdline, cmdlength); cmdlength := cmdlength + 1; end else begin cmdlength := 1; while not eoln do begin if cmdlength < cmdlinelength - 2 then begin read(input, cmdline[cmdlength]); cmdlength := cmdlength + 1; end else get(input); end; end; for i := cmdlength to cmdlinelength do cmdline[i] := ' '; end; {getcmdline} procedure scancmdline; var g, p: 0..cmdlinelength; {get and put pointers} c: char; {conversion buffer} begin {scan the command line, converting to upper case, and removing all except file names. This also sets qualifiers and checks file name syntax (at a low level, admittedly)} g := 1; p := 0; while g <= cmdlength do if cmdline[g] = '/' then begin g := g + 1; if cmdline[g] in ['e', 'E'] then extern_count := 1; while cmdline[g] in ['a'..'z', 'A'..'Z', '0'..'9'] do g := g + 1; end else if cmdline[g] in [' ', chr(ht)] then g := g + 1 else begin c := cmdline[g]; if c = '(' then c := '[' else if c = ')' then c := ']' else if c in ['a'..'z'] then c := chr(ord(c) - ord('a') + ord('A')); p := p + 1; fileline[p] := c; g := g + 1; end; fileline[p + 1] := ' '; nextf := 1; dev_start := 2; dev_end := 1; uic_start := 2; uic_end := 1; end; {scancmdline} procedure next_file(var name: file_name; {next file name found} var len: integer; {length of file name found} var more: boolean {there are more files available} ); var i: integer; {induction var} field_start: cmdindex; {start of an alphanumeric field} begin {Get the next input file from the command line} if fileline[nextf] in ['=', ','] then nextf := nextf + 1; field_start := nextf; while fileline[nextf] in ['A'..'Z', '0'..'9', '.', '$'] do nextf := nextf + 1; if fileline[nextf] = ':' then begin dev_start := field_start; field_start := cmdlinelength; dev_end := nextf; nextf := nextf + 1; end; if fileline[nextf] = ']' then begin uic_start := nextf; repeat nextf := nextf + 1; until fileline[nextf] = ']'; uic_end := nextf; nextf := nextf + 1; end; if fileline[nextf] in ['A'..'Z', '.', '$', '0'..'9'] then begin field_start := nextf; while fileline[nextf] in ['A'..'Z', '.', '$', '0'..'9'] do nextf := nextf + 1; end; len := 0; for i := dev_start to dev_end do begin len := len + 1; name[len] := fileline[i]; end; for i := uic_start to uic_end do begin len := len + 1; name[len] := fileline[i]; end; for i := field_start to nextf - 1 do begin len := len + 1; name[len] := fileline[i]; end; for i := len + 1 to file_name_len do name[i] := ' '; more := fileline[nextf] = ','; if not more then begin dev_end := dev_start - 1; uic_end := uic_start - 1; end; end; procedure initialize; begin messages_this_line := []; messages := []; file_printed := false; line_len := 0; line_no := 0; endline := false; extern_count := 0; getcmdline; scancmdline; next_file(current_file, file_len, more_files); rewrite(result, current_file, '.CVR'); next_file(current_file, file_len, more_files); reset(source, current_file, '.PAS'); writeln(result, 'Oregon Software Conversion Aid - Version 2.1D'); writeln(result, cmdline: cmdlength); writeln(result); end; {initialize} procedure print_line; var this_msg: message; {induction var} begin line_no := line_no + 1; if messages_this_line <> [] then begin if not file_printed then begin writeln(result); writeln(result, 'File: ', current_file: file_len); writeln(result); file_printed := true; end; writeln(result, line_no: 6, ': ', this_line: line_len); for this_msg := embed_switch to undesc_variant do if this_msg in messages_this_line then begin write(result, ' ***** '); case this_msg of embed_switch: writeln(result, 'Pascal 1 embedded switches.'); embed_code: writeln(result, 'Embedded macro code.'); extern_ref: writeln(result, 'External procedure reference or definition.'); origin_used: writeln(result, 'Origin''ed variable.'); undesc_variant: writeln(result, 'Undescriminated variant.'); end; end; messages := messages + messages_this_line; messages_this_line := []; end; end; {print_line} procedure mark_line(msg: message); begin {mark an error on the line} messages_this_line := messages_this_line + [msg]; end; {mark_line} procedure next_ch; begin {Print the current input line if needed, then get the next character. If the current file has been exhausted, this procedure gets a new one from the command line. If all files have been exhausted, the routine aborts with a goto to label 99. As the line is read, it is saved in "this_line" for later printing if needed.} if endline then begin print_line; line_len := 0; endline := false; readln(source); if eof(source) then begin close(source); if more_files then next_file(current_file, file_len, more_files) else goto 99; reset(source, current_file, '.PAS'); endline := false; line_no := 0; file_printed := false; end; end; if eoln(source) then begin endline := true; ch := ' '; end else begin read(source, ch); if line_len < max_line_len then begin line_len := line_len + 1; this_line[line_len] := ch; end; end; end; {next_ch} procedure next_sym; var done: boolean; {done with this scan} id: identifier; {id being built} id_char: 0..max_id; {characters read so far} end_ch: char; {end character for comment scan} procedure check_id; begin {see if the id is one of our few reserved words} sym := ident; case id_char of 3: if id = qend then sym := endsy else if id = qvar then sym := varsy; 4: if id = qtype then sym := typesy else if id = qcase then sym := casesy; 5: if id = qbegin then sym := beginsy; 6: if id = qorigin then sym := originsy; 7: if id = qforward then sym := forwardsy else if id = qfortran then sym := fortransy; 8: if id = qfunc then sym := funcsy else if id = qextern then sym := externsy; 9: if id = qproc then sym := procsy; otherwise; end; end; {check_id} procedure switches; var opt_char: char; {switch option character just read} begin {scan pascal-1 switches} repeat next_ch; if ch in ['A', 'C', 'D', 'E', 'F', 'L', 'S', 'T', 'X'] then begin opt_char := ch; next_ch; if opt_char = 'E' then begin if ch = '-' then extern_count := extern_count - 1 else extern_count := extern_count + 1; end else if opt_char = 'C' then mark_line(embed_code) else mark_line(embed_switch); next_ch; end; until ch <> ','; end; {switches} begin {Scan for the next interesting symbol, of which there are very few.} sym := nonesy; repeat if ch in ['A'..'Z', 'a'..'z'] then begin id_char := 0; id := blank_id; while ch in ['A'..'Z', 'a'..'z', '0'..'9'] do begin if id_char < max_id then begin id_char := id_char + 1; if ch in ['A'..'Z'] then id[id_char] := chr(ord(ch) - ord('A') + ord('a')) else id[id_char] := ch; end; next_ch; end; check_id; end else if ch = '''' then begin next_ch; repeat while ch <> '''' do next_ch; next_ch; until ch <> '''' end else if ch = '{' then begin next_ch; if ch = '$' then switches; while ch <> '}' do next_ch; next_ch; end else if ch in ['(', '/'] then begin if ch = '(' then end_ch := ')' else end_ch := '/'; next_ch; if ch = '*' then begin next_ch; if ch = '$' then switches; while ch <> end_ch do begin while ch <> '*' do next_ch; next_ch; end; next_ch; end else if end_ch = ')' then sym := lpar; end else if ch = ')' then begin sym := rpar; next_ch; end else if ch = ':' then begin next_ch; if ch <> '=' then sym := colon end else next_ch; until sym <> nonesy; end; {next_sym} procedure skip_until(end_syms: symbol_set); begin {scan until a symbol in end_syms is found} while not (sym in end_syms) do next_sym; end; procedure scan_decl; begin {scan declarations} next_sym; repeat skip_until([procsy, funcsy, beginsy, casesy, originsy]); if sym = casesy then begin next_sym; next_sym; if sym <> colon then mark_line(undesc_variant); end else if sym = originsy then begin mark_line(origin_used); next_sym; end; until sym in [procsy, funcsy, beginsy]; end; {scan_decl} procedure proc_header(level: integer); var paren_count: 0..maxint; {parenthesis level counter} begin {scan off a proc header at the level given specified} paren_count := 0; if (level = 1) and (extern_count > 0) then mark_line(extern_ref); next_sym; repeat next_sym; if sym = lpar then paren_count := paren_count + 1 else if sym = rpar then paren_count := paren_count - 1; until paren_count = 0; end; {proc_header} procedure body; var begin_count: 0..maxint; {begin/case nesting level} begin {scan off a body} begin_count := 1; repeat next_sym; if sym in [beginsy, casesy] then begin_count := begin_count + 1 else if sym = endsy then begin_count := begin_count - 1; until begin_count = 0; end; {body} procedure scan_program; var level: 0..maxint; {current procedure nesting level} begin {scan a program, looking for potential problems} level := 1; next_ch; next_sym; repeat skip_until([varsy, typesy, procsy, funcsy, beginsy, externsy, forwardsy, fortransy]); case sym of varsy, typesy: scan_decl; procsy, funcsy: begin proc_header(level); level := level + 1; end; externsy: begin mark_line(extern_ref); level := level - 1; next_sym; end; fortransy, forwardsy: begin level := level - 1; next_sym; end; beginsy: begin body; level := level - 1; end; end; until level = 0; end; {scan_program} procedure print_summary; begin writeln(result); if extern_ref in messages then writeln(result, 'External routines used.'); if messages * [embed_code, origin_used, undesc_variant] <> [] then writeln(result, 'Possible low-level coding techniques noted.'); if messages * [extern_ref, embed_code, origin_used, undesc_variant] = [] then writeln(result, 'No external routines or low-level techniques noted.'); end; {print_summary} begin {convrs} initialize; scan_program; 99: print_summary; end. {convrs}