{ $Id$ Copyright (c) 1998-2000 by Florian Klaempfl Parses variable declarations. Used for var statement and record definitions This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } unit pdecvar; {$i defines.inc} interface procedure read_var_decs(is_record,is_object,is_threadvar:boolean); implementation uses { common } cutils, { global } globtype,globals,tokens,verbose, systems, { symtable } symconst,symbase,symtype,symdef,symsym,symtable,types,fmodule, { pass 1 } node, nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw, { parser } scanner, pbase,pexpr,ptype,ptconst,pdecsub, { link } import; const variantrecordlevel : longint = 0; procedure read_var_decs(is_record,is_object,is_threadvar:boolean); { reads the filed of a record into a } { symtablestack, if record=false } { variants are forbidden, so this procedure } { can be used to read object fields } { if absolute is true, ABSOLUTE and file } { types are allowed } { => the procedure is also used to read } { a sequence of variable declaration } procedure insert_syms(st : tsymtable;sc : tidstringlist;tt : ttype;is_threadvar : boolean); { inserts the symbols of sc in st with def as definition or sym as ttypesym, sc is disposed } var s : string; filepos : tfileposinfo; ss : tvarsym; begin filepos:=akttokenpos; while not sc.empty do begin s:=sc.get(akttokenpos); ss:=tvarsym.Create(s,tt); if is_threadvar then include(ss.varoptions,vo_is_thread_var); st.insert(ss); { static data fields are inserted in the globalsymtable } if (st.symtabletype=objectsymtable) and (sp_static in current_object_option) then begin s:='$'+lower(st.name^)+'_'+upper(s); st.defowner.owner.insert(tvarsym.create(s,tt)); end; end; {$ifdef fixLeaksOnError} if strContStack.pop <> sc then writeln('problem with strContStack in pdecl (2)'); {$endif fixLeaksOnError} sc.free; akttokenpos:=filepos; end; var sc : tidstringList; s : stringid; old_block_type : tblock_type; declarepos,storetokenpos : tfileposinfo; oldsymtablestack : tsymtable; symdone : boolean; { to handle absolute } abssym : tabsolutesym; { c var } newtype : ttypesym; is_dll, is_gpc_name,is_cdecl, extern_aktvarsym,export_aktvarsym : boolean; old_current_object_option : tsymoptions; dll_name, C_name : string; tt,casetype : ttype; { Delphi initialized vars } tconstsym : ttypedconstsym; { maxsize contains the max. size of a variant } { startvarrec contains the start of the variant part of a record } usedalign, maxsize,minalignment,maxalignment,startvarrecalign,startvarrecsize : longint; pt : tnode; srsym : tsym; srsymtable : tsymtable; unionsymtable : tsymtable; offset : longint; uniondef : trecorddef; unionsym : tvarsym; uniontype : ttype; dummysymoptions : tsymoptions; begin old_current_object_option:=current_object_option; { all variables are public if not in a object declaration } if not is_object then current_object_option:=[sp_public]; old_block_type:=block_type; block_type:=bt_type; is_gpc_name:=false; { Force an expected ID error message } if not (token in [_ID,_CASE,_END]) then consume(_ID); { read vars } while (token=_ID) and not(is_object and (idtoken in [_PUBLIC,_PRIVATE,_PUBLISHED,_PROTECTED])) do begin C_name:=orgpattern; sc:=consume_idlist; {$ifdef fixLeaksOnError} strContStack.push(sc); {$endif fixLeaksOnError} consume(_COLON); if (m_gpc in aktmodeswitches) and not(is_record or is_object or is_threadvar) and (token=_ID) and (orgpattern='__asmname__') then begin consume(_ID); C_name:=get_stringconst; Is_gpc_name:=true; end; { this is needed for Delphi mode at least but should be OK for all modes !! (PM) } ignore_equal:=true; if is_record then begin { for records, don't search the recordsymtable for the symbols of the types } oldsymtablestack:=symtablestack; symtablestack:=symtablestack.next; read_type(tt,''); symtablestack:=oldsymtablestack; end else read_type(tt,''); { types that use init/final are not allowed in variant parts, but classes are allowed } if (variantrecordlevel>0) and (tt.def.needs_inittable and not is_class(tt.def)) then Message(parser_e_cant_use_inittable_here); ignore_equal:=false; symdone:=false; if is_gpc_name then begin storetokenpos:=akttokenpos; s:=sc.get(akttokenpos); if not sc.empty then Message(parser_e_absolute_only_one_var); {$ifdef fixLeaksOnError} if strContStack.pop <> sc then writeln('problem with strContStack in pdecl (3)'); {$endif fixLeaksOnError} sc.free; aktvarsym:=tvarsym.create_C(s,target_info.Cprefix+C_name,tt); include(aktvarsym.varoptions,vo_is_external); symtablestack.insert(aktvarsym); akttokenpos:=storetokenpos; symdone:=true; end; { check for absolute } if not symdone and (idtoken=_ABSOLUTE) and not(is_record or is_object or is_threadvar) then begin consume(_ABSOLUTE); { only allowed for one var } s:=sc.get(declarepos); if not sc.empty then Message(parser_e_absolute_only_one_var); {$ifdef fixLeaksOnError} if strContStack.pop <> sc then writeln('problem with strContStack in pdecl (4)'); {$endif fixLeaksOnError} sc.free; { parse the rest } pt:=expr; if (pt.nodetype=stringconstn) or (is_constcharnode(pt)) then begin storetokenpos:=akttokenpos; akttokenpos:=declarepos; abssym:=tabsolutesym.create(s,tt); if pt.nodetype=stringconstn then s:=strpas(tstringconstnode(pt).value_str) else s:=chr(tordconstnode(pt).value); consume(token); abssym.abstyp:=toasm; abssym.asmname:=stringdup(s); symtablestack.insert(abssym); akttokenpos:=storetokenpos; symdone:=true; end; if not symdone then begin { variable } if (pt.nodetype=loadn) then begin { we should check the result type of srsym } if not (tloadnode(pt).symtableentry.typ in [varsym,typedconstsym,funcretsym]) then Message(parser_e_absolute_only_to_var_or_const); storetokenpos:=akttokenpos; akttokenpos:=declarepos; abssym:=tabsolutesym.create(s,tt); abssym.abstyp:=tovar; abssym.ref:=tstoredsym(tloadnode(pt).symtableentry); symtablestack.insert(abssym); akttokenpos:=storetokenpos; symdone:=true; end { funcret } else if (pt.nodetype=funcretn) then begin storetokenpos:=akttokenpos; akttokenpos:=declarepos; abssym:=tabsolutesym.create(s,tt); abssym.abstyp:=tovar; abssym.ref:=tstoredsym(tfuncretnode(pt).funcretsym); symtablestack.insert(abssym); akttokenpos:=storetokenpos; symdone:=true; end; { address } if (not symdone) and ((target_info.target=target_i386_go32v2) or (m_objfpc in aktmodeswitches) or (m_delphi in aktmodeswitches)) then begin if is_constintnode(pt) then begin storetokenpos:=akttokenpos; akttokenpos:=declarepos; abssym:=tabsolutesym.create(s,tt); abssym.abstyp:=toaddr; abssym.absseg:=false; abssym.address:=tordconstnode(pt).value; if (token=_COLON) and (target_info.target=target_i386_go32v2) then begin consume(token); pt:=expr; if is_constintnode(pt) then begin abssym.address:=abssym.address shl 4+tordconstnode(pt).value; abssym.absseg:=true; end else Message(parser_e_absolute_only_to_var_or_const); end; symtablestack.insert(abssym); akttokenpos:=storetokenpos; end else Message(parser_e_absolute_only_to_var_or_const); end; end else Message(parser_e_absolute_only_to_var_or_const); symdone:=true; end; { Handling of Delphi typed const = initialized vars ! } { When should this be rejected ? - in parasymtable - in record or object - ... (PM) } if (token=_EQUAL) and not(m_tp7 in aktmodeswitches) and not(symtablestack.symtabletype in [parasymtable]) and not is_record and not is_object then begin storetokenpos:=akttokenpos; s:=sc.get(akttokenpos); if not sc.empty then Message(parser_e_initialized_only_one_var); tconstsym:=ttypedconstsym.createtype(s,tt,true); symtablestack.insert(tconstsym); akttokenpos:=storetokenpos; consume(_EQUAL); readtypedconst(tt,tconstsym,true); symdone:=true; end; { hint directive } {$warning hintdirective not stored in syms} dummysymoptions:=[]; try_consume_hintdirective(dummysymoptions); { for a record there doesn't need to be a ; before the END or ) } if not((is_record or is_object) and (token in [_END,_RKLAMMER])) then consume(_SEMICOLON); { procvar handling } if (tt.def.deftype=procvardef) and (tt.def.typesym=nil) then begin newtype:=ttypesym.create('unnamed',tt); parse_var_proc_directives(tsym(newtype)); newtype.restype.def:=nil; tt.def.typesym:=nil; newtype.free; end; { Check for variable directives } if not symdone and (token=_ID) then begin { Check for C Variable declarations } if (m_cvar_support in aktmodeswitches) and not(is_record or is_object or is_threadvar) and (idtoken in [_EXPORT,_EXTERNAL,_PUBLIC,_CVAR]) then begin { only allowed for one var } s:=sc.get(declarepos); if not sc.empty then Message(parser_e_absolute_only_one_var); {$ifdef fixLeaksOnError} if strContStack.pop <> sc then writeln('problem with strContStack in pdecl (5)'); {$endif fixLeaksOnError} sc.free; { defaults } is_dll:=false; is_cdecl:=false; extern_aktvarsym:=false; export_aktvarsym:=false; { cdecl } if idtoken=_CVAR then begin consume(_CVAR); consume(_SEMICOLON); is_cdecl:=true; C_name:=target_info.Cprefix+C_name; end; { external } if idtoken=_EXTERNAL then begin consume(_EXTERNAL); extern_aktvarsym:=true; end; { export } if idtoken in [_EXPORT,_PUBLIC] then begin consume(_ID); if extern_aktvarsym or (symtablestack.symtabletype in [parasymtable,localsymtable]) then Message(parser_e_not_external_and_export) else export_aktvarsym:=true; end; { external and export need a name after when no cdecl is used } if not is_cdecl then begin { dll name ? } if (extern_aktvarsym) and (idtoken<>_NAME) then begin is_dll:=true; dll_name:=get_stringconst; end; consume(_NAME); C_name:=get_stringconst; end; { consume the ; when export or external is used } if extern_aktvarsym or export_aktvarsym then consume(_SEMICOLON); { insert in the symtable } storetokenpos:=akttokenpos; akttokenpos:=declarepos; if is_dll then aktvarsym:=tvarsym.create_dll(s,tt) else aktvarsym:=tvarsym.create_C(s,C_name,tt); { set some vars options } if export_aktvarsym then begin inc(aktvarsym.refs); include(aktvarsym.varoptions,vo_is_exported); end; if extern_aktvarsym then include(aktvarsym.varoptions,vo_is_external); { insert in the stack/datasegment } symtablestack.insert(aktvarsym); akttokenpos:=storetokenpos; { now we can insert it in the import lib if its a dll, or add it to the externals } if extern_aktvarsym then begin if is_dll then begin if not(current_module.uses_imports) then begin current_module.uses_imports:=true; importlib.preparelib(current_module.modulename^); end; importlib.importvariable(aktvarsym.mangledname,dll_name,C_name) end else if target_info.DllScanSupported then current_module.Externals.insert(tExternalsItem.create(aktvarsym.mangledname)); end; symdone:=true; end else if (is_object) and (cs_static_keyword in aktmoduleswitches) and (idtoken=_STATIC) then begin include(current_object_option,sp_static); insert_syms(symtablestack,sc,tt,false); exclude(current_object_option,sp_static); consume(_STATIC); consume(_SEMICOLON); symdone:=true; end; end; { insert it in the symtable, if not done yet } if not symdone then begin { save object option, because we can turn of the sp_published } if (sp_published in current_object_option) and not(is_class(tt.def)) then begin Message(parser_e_cant_publish_that); exclude(current_object_option,sp_published); end else if (sp_published in current_object_option) and not(oo_can_have_published in tobjectdef(tt.def).objectoptions) then begin Message(parser_e_only_publishable_classes_can__be_published); exclude(current_object_option,sp_published); end; insert_syms(symtablestack,sc,tt,is_threadvar); current_object_option:=old_current_object_option; end; end; { Check for Case } if is_record and (token=_CASE) then begin maxsize:=0; maxalignment:=0; consume(_CASE); s:=pattern; searchsym(s,srsym,srsymtable); { may be only a type: } if assigned(srsym) and (srsym.typ in [typesym,unitsym]) then begin { for records, don't search the recordsymtable for the symbols of the types } oldsymtablestack:=symtablestack; symtablestack:=symtablestack.next; read_type(casetype,''); symtablestack:=oldsymtablestack; end else begin consume(_ID); consume(_COLON); { for records, don't search the recordsymtable for the symbols of the types } oldsymtablestack:=symtablestack; symtablestack:=symtablestack.next; read_type(casetype,''); symtablestack:=oldsymtablestack; symtablestack.insert(tvarsym.create(s,casetype)); end; if not(is_ordinal(casetype.def)) or is_64bitint(casetype.def) then Message(type_e_ordinal_expr_expected); consume(_OF); UnionSymtable:=trecordsymtable.create; Unionsymtable.next:=symtablestack; registerdef:=false; UnionDef:=trecorddef.create(unionsymtable); registerdef:=true; symtablestack:=UnionSymtable; startvarrecsize:=symtablestack.datasize; startvarrecalign:=symtablestack.dataalignment; repeat repeat pt:=comp_expr(true); if not(pt.nodetype=ordconstn) then Message(cg_e_illegal_expression); pt.free; if token=_COMMA then consume(_COMMA) else break; until false; consume(_COLON); { read the vars } consume(_LKLAMMER); inc(variantrecordlevel); if token<>_RKLAMMER then read_var_decs(true,false,false); dec(variantrecordlevel); consume(_RKLAMMER); { calculates maximal variant size } maxsize:=max(maxsize,symtablestack.datasize); maxalignment:=max(maxalignment,symtablestack.dataalignment); { the items of the next variant are overlayed } symtablestack.datasize:=startvarrecsize; symtablestack.dataalignment:=startvarrecalign; if (token<>_END) and (token<>_RKLAMMER) then consume(_SEMICOLON) else break; until (token=_END) or (token=_RKLAMMER); { at last set the record size to that of the biggest variant } symtablestack.datasize:=maxsize; symtablestack.dataalignment:=maxalignment; uniontype.def:=uniondef; uniontype.sym:=nil; UnionSym:=tvarsym.create('case',uniontype); symtablestack:=symtablestack.next; { we do NOT call symtablestack.insert on purpose PM } if aktalignment.recordalignmax=-1 then begin {$ifdef i386} if maxalignment>2 then minalignment:=4 else if maxalignment>1 then minalignment:=2 else minalignment:=1; {$else} {$ifdef m68k} minalignment:=2; {$endif} minalignment:=1; {$endif} end else minalignment:=maxalignment; usedalign:=used_align(maxalignment,minalignment,maxalignment); offset:=align(symtablestack.datasize,usedalign); symtablestack.datasize:=offset+unionsymtable.datasize; if maxalignment>symtablestack.dataalignment then symtablestack.dataalignment:=maxalignment; trecordsymtable(Unionsymtable).Insert_in(symtablestack,offset); Unionsym.owner:=nil; unionsym.free; uniondef.free; end; block_type:=old_block_type; current_object_option:=old_current_object_option; end; end. { $Log$ Revision 1.22 2001-11-20 18:48:26 peter * fixed initialized variables Revision 1.21 2001/10/23 21:49:42 peter * $calling directive and -Cc commandline patch added from Pavel Ozerski Revision 1.20 2001/09/30 21:15:48 peter * merged absolute support for constants Revision 1.19 2001/08/30 20:13:53 peter * rtti/init table updates * rttisym for reusable global rtti/init info * support published for interfaces Revision 1.18 2001/07/01 20:16:16 peter * alignmentinfo record added * -Oa argument supports more alignment settings that can be specified per type: PROC,LOOP,VARMIN,VARMAX,CONSTMIN,CONSTMAX,RECORDMIN RECORDMAX,LOCALMIN,LOCALMAX. It is possible to set the mimimum required alignment and the maximum usefull alignment. The final alignment will be choosen per variable size dependent on these settings Revision 1.17 2001/06/03 21:57:36 peter + hint directive parsing support Revision 1.16 2001/04/18 22:01:57 peter * registration of targets and assemblers Revision 1.15 2001/04/13 01:22:12 peter * symtable change to classes * range check generation and errors fixed, make cycle DEBUG=1 works * memory leaks fixed Revision 1.14 2001/04/04 22:43:52 peter * remove unnecessary calls to firstpass Revision 1.13 2001/04/04 21:30:45 florian * applied several fixes to get the DD8 Delphi Unit compiled e.g. "forward"-interfaces are working now Revision 1.12 2001/04/02 21:20:33 peter * resulttype rewrite Revision 1.11 2001/03/11 22:58:50 peter * getsym redesign, removed the globals srsym,srsymtable Revision 1.10 2001/03/06 18:28:02 peter * patch from Pavel with a new and much faster DLL Scanner for automatic importing so $linklib works for DLLs. Thanks Pavel! Revision 1.9 2001/02/20 21:42:54 peter * record and object declaration with same field as type fixed Revision 1.7 2001/02/20 11:19:45 marco * Fix passing tvarrec to array of const Revision 1.6 2000/12/25 00:07:27 peter + new tlinkedlist class (merge of old tstringqueue,tcontainer and tlinkedlist objects) Revision 1.5 2000/12/17 14:00:18 peter * fixed static variables Revision 1.4 2000/11/29 00:30:36 florian * unused units removed from uses clause * some changes for widestrings Revision 1.3 2000/11/04 14:25:20 florian + merged Attila's changes for interfaces, not tested yet Revision 1.2 2000/10/31 22:02:49 peter * symtable splitted, no real code changes Revision 1.1 2000/10/14 10:14:51 peter * moehrendorf oct 2000 rewrite }