home *** CD-ROM | disk | FTP | other *** search
- {TUSEMS -- semantics stuff for simple Turbo compiler }
-
- {**********************}
- procedure TAB;
- begin
- if llen<opcd_position then begin
- write(rfile, ' ':opcd_position-llen);
- llen:=opcd_position;
- end
- else
- if llen<opnd_position then begin
- write(rfile, ' ':opnd_position-llen);
- llen:=opnd_position;
- end
- else begin
- write(rfile, ' ');
- llen:=llen+1;
- end
- end;
-
- {***********************}
- procedure WRCODE(STR: string31);
- var SX: int;
- begin
- sx:=1;
- while sx<=length(str) do begin
- if str[sx]=' ' then begin
- tab;
- while str[sx]=' ' do sx:=sx+1;
- end
- else begin
- write(rfile, str[sx]);
- llen:=llen+1;
- sx:=sx+1;
- end
- end;
- if length(comment)>0 then begin
- writeln(rfile, ' ; ', comment);
- comment:='';
- end
- else writeln(rfile);
- llen:=0;
- end;
-
- {*******************}
- procedure CODE(STR: string31);
- begin
- tab;
- wrcode(str);
- end;
-
- {*******************}
- procedure CODE2(S1, S2: string31);
- begin
- code(concat(s1, s2));
- end;
-
- {*******************}
- procedure CODE3(S1, S2, S3: string31);
- begin
- code(concat(s1, concat(s2, s3)));
- end;
-
- {*******************}
- procedure CODE4(S1, S2, S3, S4: string31);
- begin
- code(concat(s1, concat(s2, concat(s3, s4))));
- end;
-
- {*******************}
- procedure LCODE(THE_LABEL, STR: string31);
- begin
- write(rfile, the_label {, ':'} );
- {NOTE: the Microsoft assembler expects
- a colon or other separator between a label and
- the operation code. CHASM doesn't}
- llen:=llen+length(the_label) {+1} ;
- tab;
- wrcode(str);
- end;
-
- {*******************}
- procedure CODESTRINGS;
- var STR: string80;
- SX: integer;
- begin
- while gslist<>nil do
- with gslist^.left^ do begin {points to a STRNG}
- str:='';
- sx:=stx;
- while strtab[sx]<>chr(0) do begin
- str:=concat(str, ' ');
- str[length(str)]:=strtab[sx];
- if strtab[sx]='''' then
- str:=concat(str, '''');
- sx:=sx+1;
- end;
- lcode(concat('SS', num2string(strngnum)),
- concat('DB ''',
- concat(str, ''',0')));
- gslist:=gslist^.right; {next list member}
- end;
- strx:=0;
- gslist:=nil;
- end;
-
- {******************}
- procedure INCL_FILE(FNAME: string31);
- var IFILE: text;
- LINE: longstring;
- begin
- writeln(rfile, ' ; <', fname, '> included');
- {$I-}
- assign(ifile, fname);
- reset(ifile); {$I+}
- if ioresult<>0 then error(concat('can''t open ', fname))
- else begin
- while not(eof(ifile)) do begin
- readln(ifile, line);
- writeln(rfile, line);
- end;
- close(ifile);
- end;
- writeln(rfile, ' ; ... end of include ', fname);
- end;
-
- {******************}
- procedure INIT_SEM;
- { Semantics initialization -- called before any productions
- are applied. }
-
- {..................}
- procedure DEF_FUNCTION(NAME: string15; PARMS: int);
- var TSYMP: symtabp;
- begin {some predefined functions}
- tsymp:=makesym(symtab, name, func_type, plevel);
- with tsymp^ do begin
- faddr:=0;
- is_system:=true;
- pbytes:=parms*varsize;
- is_actual:=true;
- end
- end;
-
- begin
- llen:=0;
- labcount:=0; {for made-up labels}
- trcount:=0;
- main_symp:=nil;
- plevel:=0;
- gslist:=nil;
- comment:='';
- next_snum:=0; {string code numbers}
- def_function('WRITE', 0); {takes integers and strings}
- def_function('READ', 0); {reads and returns an integer}
- def_function('WRITELN', 0); {takes integers and strings}
- def_function('EOF', 0); {returns 1 if end of file, 0 otherwise}
- def_function('HALT', 0); {stops process}
- writeln(rfile, ' ; Tiny Pascal assembler code');
- code('MOV SP,OFFSET(STACKORG)'); {set stack pointer}
- code('MOV BP,SP'); {and marker pointer}
- code('CALL MAIN'); {must be name of some procedure}
- code('INT 020H'); {return to system}
- incl_file('STDIO.HDR'); {system procedures}
- end;
-
- {******************}
- procedure END_SEM;
- var HX: integer;
- TSYMP: symtabp;
- begin
- {dump global variables}
- writeln(rfile, ' ; GLOBAL VARIABLES');
- for hx:=0 to hlimit do begin
- tsymp:=symtab[hx];
- while tsymp<>nil do
- with tsymp^ do begin
- if (level=0) and
- (symt=var_type) then
- lcode(sym, 'DW 0');
- tsymp:=next;
- end
- end;
- writeln(rfile, ' ; RUNTIME STACK');
- code('DS 2000');
- lcode('STACKORG', 'DW 0'); {bottom of stack}
- if main_symp=nil then
- error('no MAIN procedure found')
- else begin
- writeln(rfile, ' ; MAIN stack space');
- for hx:=1 to (main_symp^.pbytes div 2)+1 do code('DW 0');
- {these allow space for the MAIN program's formal parameters}
- end;
- if errors>0 then begin
- writeln(rfile, '; **** ', errors:1, ' error(s) seen');
- writeln('**** ', errors:1, ' error(s) seen');
- end
- else begin
- writeln(rfile, '; NO errors');
- writeln('NO errors');
- end
- end;
-
- {******************}
- function NEW_SEM {(SEMTYP: semtype): semrecp} ;
- var TSEMP, TSEMP1: semrecp;
- begin
- new(tsemp);
- new_sem:=tsemp;
- with tsemp^ do begin
- semt:=semtyp;
- case semtyp of
- ident: symp:=nil;
- fixed: numval:=0;
- strng: begin
- stx:=strx;
- strngnum:=next_snum;
- next_snum:=next_snum+1;
- tsemp1:=new_sem(stmtlist);
- with tsemp1^ do begin {link into a global list of strings}
- left:=tsemp;
- right:=gslist;
- end;
- gslist:=tsemp1;
- end;
- addop..funcall: begin
- left:=nil;
- right:=nil;
- end;
- if_then_else: begin
- b1:=nil;
- s1:=nil;
- s2:=nil;
- end;
- ELSE ;
- end
- end
- end;
-
- {************************}
- procedure TRACEIT(TRACE: boolean; MSG: string80; SEMP: semrecp);
- var STR: longstring;
- begin
- if trace then begin
- write(rfile, ' ':trcount, msg, ': ');
- if semp<>nil then begin
- write(rfile, sem_names[semp^.semt]);
- if semp^.semt=ident then begin
- write(rfile, ': ', semp^.symp^.sym);
- end
- end
- else write(rfile, sem_names[other]);
- writeln(rfile);
- trcount:=trcount+2;
- end
- end;
-
- {************************}
- procedure ENDIT(TRACE: boolean);
- begin
- if trace then begin
- trcount:=trcount-2;
- writeln(rfile, ' ':trcount, '[exit]');
- end
- end;
-
- {************************}
- procedure DUMPTREE(MSG: string80; ROOT: semrecp);
- var CH: char;
- begin
- if trace then begin
- write(rfile, '***DUMP ', msg, ' ');
- dump_sem(2, root, '');
- writeln(rfile);
- writeln(rfile, '***END');
- end
- end;
-
- {***********************}
- procedure DISP_SEM(TSEMP: semrecp);
- begin {recursively dispose a SEMREC tree}
- if tsemp<>nil then
- with tsemp^ do begin
- case semt of
- addop..funcall: begin
- disp_sem(left);
- disp_sem(right);
- end;
- if_then_else: begin
- disp_sem(b1);
- disp_sem(s1);
- disp_sem(s2);
- end;
- ELSE ;
- end;
- dispose(tsemp);
- end
- end;
-
- {************************}
- function OPCODE(SEMT: semtype): string8;
- begin
- case semt of
- addop: opcode:='ADD';
- subop: opcode:='SUB';
- mpyop: opcode:='IMULW';
- divop: opcode:='IDIVW';
- end
- end;
-
- {***********************}
- function IS_SIMPLE(ROOT: semrecp): boolean;
- begin
- if root^.semt=fixed then is_simple:=true
- else
- if root^.semt=ident then
- is_simple:=(root^.symp^.symt=var_type)
- else is_simple:=false;
- end;
-
- {***********************}
- function NAMEOF(ROOT: semrecp): string15;
- begin {ROOT has to be an IDENT or a FIXED;
- this returns a string that will go into an
- appropriate instruction location}
- with root^ do begin
- nameof:=''; {default}
- if semt=fixed then nameof:=num2string(numval)
- else
- if semt=ident then begin
- with symp^ do begin
- if length(comment)>0 then
- comment:=concat(comment, ', ');
- comment:=concat(comment, symp^.sym);
- case symt of
- var_type: if level=0 then nameof:=sym
- else nameof:=concat(num2string(vaddr), '[BP]');
- func_type: nameof:=
- concat(num2string(pbytes+2*varsize), '[BP]');
- user: symerror(sym, 'undeclared variable');
- ELSE ;
- end
- end
- end
- else error('BUG2: nameof');
- end
- end;
-
- {***********************}
- function NEW_LABEL: string8;
- begin
- new_label:=concat('XXX', num2string(labcount));
- labcount:=labcount+1;
- end;
-
- procedure EVAL (ROOT: semrecp); forward;
-
- {************************}
- procedure CODE_USER(ROOT: semrecp);
- var FPARM: semrecp;
- POSITION: integer;
- begin {an ordinary user procedure}
- code('PUSH AX'); {place for return value}
- fparm:=root^.right;
- position:=0;
- while fparm<>nil do
- with fparm^ do begin
- eval(left); {one parameter to AX}
- code('PUSH AX'); {push it on the stack}
- if left^.semt=strng then
- error('string parameter is invalid');
- position:=position+varsize;
- fparm:=fparm^.right;
- end;
- with root^.left^.symp^ do begin
- if (position>pbytes) then
- error('too many actual parameters')
- else
- if (position<pbytes) then begin
- code('MOV AX,0');
- while (position<pbytes) do begin
- {add more parameter places for local variables}
- code('PUSH AX');
- position:=position+varsize;
- end
- end;
- code2('CALL ', sym);
- end
- end;
-
- {*************************}
- procedure CODE_SYSTEM(ROOT: semrecp);
- var FPARM: semrecp;
- begin {system procedure -- broken into
- unit calls, but at high level takes
- an arbitrary number of mixed integers
- and string addresses}
- {The only one we have so far is WRITE/WRITELN}
- with root^.left^.symp^ do
- if (sym='WRITE') or
- (sym='WRITELN') then begin
- fparm:=root^.right; {formal parameter list}
- while fparm<>nil do
- with fparm^.left^ do begin
- if semt=strng then begin
- code3('MOV BX,OFFSET(SS', num2string(strngnum), ')');
- {address to BX}
- code('CALL SYS_SWRT');
- end
- else begin
- eval(fparm^.left); {integer left over in AX}
- code('CALL SYS_IWRT');
- end;
- fparm:=fparm^.right;
- end;
- if sym='WRITELN' then
- code('CALL SYS_WRTLN');
- end
- else if sym='HALT' then code('INT 020H')
- else if sym='READ' then code('CALL READ')
- else symerror(sym, 'missing system procedure');
- end;
-
- {***********************}
- procedure CODE_FUNCALL(ROOT: semrecp);
- var FPARM: semrecp;
- POS: integer;
- FROOT: semrecp;
- begin
- if root^.semt=ident then begin
- froot:=new_sem(funcall);
- froot^.left:=root;
- code_funcall(froot);
- end
- else
- if root^.left^.symp^.is_system then code_system(root)
- else code_user(root);
- end;
-
- {************************}
- procedure EVAL {(ROOT: semrecp)} ;
- var LABEL1, LABEL2: string8;
- begin
- if root<>nil then
- with root^ do
- case semt of
- ident: if symp^.symt=var_type then code2('MOV AX,', nameof(root))
- else if symp^.symt=func_type then code_funcall(root)
- else symerror(symp^.sym, 'invalid as an expression');
- fixed: code2('MOV AX,', num2string(numval));
- strng: code2('MOV AX,SS', num2string(stx));
- addop, subop:
- if is_simple(right) then begin
- eval(left); {goes to AX}
- code3(opcode(semt), ' AX,', nameof(right));
- end
- else begin
- eval(right);
- code('PUSH AX'); {put in stack temporarily}
- eval(left); {left side to AX}
- code('POP DX'); {get the right value back from stack}
- code2(opcode(semt), ' AX,DX');
- end;
- mpyop, divop: begin
- eval(right); {divisor to AX}
- code('PUSH AX');
- eval(left); {dividend to AX}
- if semt=divop then code('CWD'); {sign extend into DX}
- code('POP CX');
- code2(opcode(semt), ' CX');
- end;
- assignop: begin
- if right^.semt=fixed then {an immediate on the right is OK}
- code4('MOVW ', nameof(left), ',', nameof(right))
- else begin
- eval(right); {goes to AX}
- code3('MOV ', nameof(left), ',AX');
- end
- end;
- while_do: begin
- label1:=new_label;
- lcode(label1, 'EQU $');
- eval(left); {boolean condition}
- code('CMP AX,0');
- label2:=new_label;
- code2('JLE ', label2);
- eval(right); {statement or statement list}
- code2('JMP ', label1);
- lcode(label2, 'EQU $');
- end;
- stmtlist:
- while root<>nil do begin
- eval(root^.left);
- root:=root^.right;
- end;
- funcall: code_funcall(root);
- if_then_else: begin
- label1:=new_label;
- eval(b1); {boolean condition}
- code('CMP AX,0');
- code2('JLE ', label1);
- eval(s1); {THEN statement}
- label2:=new_label;
- code2('JMP ', label2);
- lcode(label1, 'EQU $');
- eval(s2); {ELSE statement}
- lcode(label2, 'EQU $');
- end
- end
- end;
-
- {***********************}
- procedure FUNC_OPEN(ID: semrecp);
- begin
- { Picture of stack just after a call:
- function return value (2 bytes)
- parm1 (2 bytes)
- parm2 (2 bytes)
- ...
- parmN (2 bytes) <-- SP+2
- return address (2 bytes) <-- SP
-
- We then push a `previous' BP, and set BP to the new SP, adding
- one more word to the stack.
- This convention is nearly like that used in Turbo, but with
- the following two exceptions:
- 1) in Turbo, parameters are pushed in reverse order, i.e.
- last parameter is pushed first and vice versa.
- 2) no stack space for a return value is provided in Turbo, as
- in our scheme.
- In both, the function's return value is returned in AX}
-
- lcode(id^.symp^.sym, 'PROC NEAR');
- {marks the procedure's entry location}
- code('PUSH BP'); {marker location}
- code('MOV BP,SP'); {set BP to current SP}
- end;
-
- {***********************}
- procedure FUNC_CLOSE(ID: semrecp);
- begin {code an EXIT operation}
- comment:=id^.symp^.sym;
- code3('MOV AX,', num2string(id^.symp^.pbytes+2*varsize), '[BP]');
- code('POP BP'); {restore BP}
- code2('RET ', num2string(id^.symp^.pbytes+varsize));
- codestrings;
- code('ENDP');
- end;
-
- {**************************}
- function LIST_SYM(SYM: symbol; POSITION: int):int;
- begin
- writeln(rfile, ' ; ', sym, ' ':(maxtoklen+2-length(sym)),
- position:1, '[BP]');
- list_sym:=position-varsize;
- end;
-
- {***********************}
- procedure LIST_SYMS(FID, FPLIST: semrecp);
- var POSITION: int;
- begin
- writeln(rfile, ' ; SYMBOL TABLE');
- position:=fid^.symp^.pbytes+4; {return value}
- position:=list_sym(fid^.symp^.sym, position);
- while fplist<>nil do begin
- position:=list_sym(fplist^.left^.symp^.sym, position);
- fplist:=fplist^.right;
- end;
- writeln(rfile);
- end;
-
- {*************************}
- procedure DECL_VARS(IDLIST: semrecp; NEXT_ADDR: int; FPS: boolean);
- begin {declare local (fps) or global ~(fps) variables}
- while idlist<>nil do begin
- if idlist^.left^.semt<>ident then
- error('need an identifier')
- else
- with idlist^.left^ do begin
- if fps then begin {locals}
- if symp^.symt=user then begin {hasn't been declared yet}
- symp^.symt:=var_type;
- symp^.level:=plevel;
- end
- else
- if symp^.level<plevel then {this shadows a global}
- symp:=forcesym(symtab, symp^.sym, var_type, plevel)
- else
- symerror(symp^.sym, 'multiply declared');
- with symp^ do begin
- vaddr:=next_addr;
- next_addr:=next_addr-varsize;
- end
- end
- else {global variables}
- with symp^ do begin
- if symt<>user then
- symerror(sym, 'multiply declared');
- symt:=var_type; {vaddr isn't needed}
- end
- end;
- idlist:=idlist^.right;
- end
- end;
-
- {************************}
- procedure DECL_FUNC(ID, PARMS, BODY: semrecp);
- var NPARMS: int;
- TP: semrecp;
- begin
- nparms:=0;
- tp:=parms;
- while tp<>nil do begin {count the parameters}
- nparms:=nparms+1;
- tp:=tp^.right;
- end;
- with id^, symp^ do begin
- if (symt=var_type) or {previously declared a variable}
- ((symt=func_type) and is_actual)
- {previously declared as a full procedure}
- then symerror(sym, 'multiply declared');
- if symt=user then begin {hasn't been seen before}
- faddr:=0;
- is_system:=false;
- end;
- symt:=func_type;
- is_actual:=(body<>nil);
- plevel:=plevel+1; {at local level for parameters}
- decl_vars(parms, varsize*(nparms+1), true);
- pbytes:=varsize*(nparms);
- if sym='MAIN' then main_symp:=symp;
- end
- end;
-
- {*********************}
- procedure APPLY(PFLAG: int; var TSEMP: semrecp);
-
- {.....................}
- function IS_ARITH(TSEMP: semrecp): boolean;
- begin
- is_arith:=tsemp^.semt in [ident, fixed, addop, subop,
- mpyop, divop, funcall];
- end;
-
- {....................}
- function IS_STRING(TSEMP: semrecp): boolean;
- begin
- is_string:=tsemp^.semt=strng;
- end;
-
- {....................}
- procedure BIN_TREE(STYPE: semtype);
- begin
- tsemp:=new_sem(stype);
- tsemp^.left:=sem[tos-2];
- tsemp^.right:=sem[tos];
- if not(is_arith(sem[tos-2]) and
- is_arith(sem[tos])) then
- error('nonarithmetic operand');
- end;
-
- {....................}
- function NCONC(STL, ST: semrecp): semrecp;
- begin {STL is a list based on the RIGHT pointer.
- It may be NIL}
- if stl=nil then nconc:=st
- else begin
- nconc:=stl;
- while stl^.right<>nil do stl:=stl^.right;
- stl^.right:=st;
- end
- end;
-
- {....................}
- function IS_VOID(TSEMP: semrecp): boolean;
- begin {look for the special identifier VOID}
- if tsemp=nil then is_void:=true
- else begin
- is_void:=false;
- if tsemp^.semt=ident then
- if tsemp^.symp^.sym='VOID' then is_void:=true;
- end
- end;
-
- begin
- case pflag of
- ADDOPR: { Expr -> Expr + Term }
- begin
- bin_tree(addop);
- end;
- ASSIGN: { Stmt -> <identifier> := Expr }
- begin
- bin_tree(assignop);
- end;
- BLOCK: { Stmt -> BEGIN StmtList END }
- begin
- tsemp:=sem[tos-1];
- end;
- DIVOPR: { Term -> Term / Primary }
- begin
- bin_tree(divop);
- end;
- EXPRLIST1: { ExprList -> Expr }
- if not(is_void(sem[tos])) then begin
- tsemp:=new_sem(expr_list);
- tsemp^.left:=sem[tos];
- end;
- EXPRLIST2: { ExprList -> ExprList , Expr }
- if not(is_void(sem[tos])) then begin
- tsemp:=new_sem(expr_list);
- tsemp^.left:=sem[tos];
- tsemp:=nconc(sem[tos-2], tsemp);
- end
- else tsemp:=sem[tos-2];
- FDECL: { FuncDecl -> FUNCTION <identifier> ( ExprList ) ; Stmt }
- begin {should be at global level}
- plevel:=0; {just in case}
- decl_func(sem[tos-5], sem[tos-3], sem[tos]);
- {... also increments PLEVEL by one}
- if sem[tos]<>nil then begin
- func_open(sem[tos-5]);
- eval(sem[tos]); {evaluate the Stmt}
- func_close(sem[tos-5]);
- list_syms(sem[tos-5], sem[tos-3]);
- end;
- disp_sem(sem[tos-5]);
- disp_sem(sem[tos-3]);
- disp_sem(sem[tos]);
- clearsym(symtab, plevel);
- plevel:=0;
- end;
- FUNCP: { Primary -> <identifier> ( ExprList ) }
- begin {function call}
- tsemp:=new_sem(funcall);
- tsemp^.left:=sem[tos-3];
- tsemp^.right:=sem[tos-1];
- end;
- IFTHEN: { Stmt -> IF Expr THEN Stmt ELSE Stmt }
- begin
- tsemp:=new_sem(if_then_else);
- with tsemp^ do begin
- b1:=sem[tos-4];
- s1:=sem[tos-2];
- s2:=sem[tos];
- if is_string(b1) then
- error('if-expr is a string');
- end
- end;
- MPYOPR: { Term -> Term * Primary }
- begin
- bin_tree(mpyop);
- end;
- PAREN: { Primary -> ( Expr ) }
- begin
- tsemp:=sem[tos-1];
- end;
- SEXPR: { Stmt -> Expr }
- begin
- if not(sem[tos]^.semt in [ident, funcall]) then
- error('invalid statement');
- {Can't check for function call since function names
- haven't been defined yet}
- end;
- STLIST2: { StmtList -> StmtList Stmt ; }
- begin
- tsemp:=new_sem(stmtlist);
- tsemp^.left:=sem[tos-1];
- tsemp:=nconc(sem[tos-2], tsemp);
- end;
- SUBOPR: { Expr -> Expr - Term }
- begin
- bin_tree(subop);
- end;
- VDECL: { FuncDecl -> VAR ExprList }
- begin
- decl_vars(sem[tos], 0, false);
- disp_sem(sem[tos]);
- end;
- WHILEDO: { Stmt -> WHILE Expr DO Stmt }
- begin
- tsemp:=new_sem(while_do);
- tsemp^.left:=sem[tos-2];
- tsemp^.right:=sem[tos];
- if is_string(tsemp^.left) then
- error('while-expr is a string');
- end
- ELSE writeln(rfile, pflag);
- error('unknown production flag');
- end { apply case };
- end;
-