mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-12 08:10:31 +02:00
1382 lines
54 KiB
ObjectPascal
1382 lines
54 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2002 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 fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
symsym,symdef;
|
|
|
|
type
|
|
tvar_dec_option=(vd_record,vd_object,vd_threadvar);
|
|
tvar_dec_options=set of tvar_dec_option;
|
|
|
|
function read_property_dec(aclass:tobjectdef):tpropertysym;
|
|
|
|
procedure read_var_decls(options:Tvar_dec_options);
|
|
|
|
procedure read_record_fields(options:Tvar_dec_options);
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils,
|
|
{ common }
|
|
cutils,cclasses,
|
|
{ global }
|
|
globtype,globals,tokens,verbose,
|
|
systems,
|
|
{ symtable }
|
|
symconst,symbase,symtype,symtable,defutil,defcmp,
|
|
fmodule,
|
|
{ pass 1 }
|
|
node,pass_1,aasmdata,
|
|
nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw,nmem,
|
|
{ codegen }
|
|
ncgutil,
|
|
{ parser }
|
|
scanner,
|
|
pbase,pexpr,ptype,ptconst,pdecsub,
|
|
{ link }
|
|
import
|
|
;
|
|
|
|
|
|
function read_property_dec(aclass:tobjectdef):tpropertysym;
|
|
|
|
{ convert a node tree to symlist and return the last
|
|
symbol }
|
|
function parse_symlist(pl:tpropaccesslist;var def:tdef):boolean;
|
|
var
|
|
idx : longint;
|
|
sym : tsym;
|
|
srsymtable : TSymtable;
|
|
st : TSymtable;
|
|
p : tnode;
|
|
begin
|
|
result:=true;
|
|
def:=nil;
|
|
if token=_ID then
|
|
begin
|
|
if assigned(aclass) then
|
|
sym:=search_class_member(aclass,pattern)
|
|
else
|
|
searchsym(pattern,sym,srsymtable);
|
|
if assigned(sym) then
|
|
begin
|
|
case sym.typ of
|
|
fieldvarsym :
|
|
begin
|
|
if not(sp_private in current_object_option) then
|
|
addsymref(sym);
|
|
pl.addsym(sl_load,sym);
|
|
def:=tfieldvarsym(sym).vardef;
|
|
end;
|
|
procsym :
|
|
begin
|
|
if not(sp_private in current_object_option) then
|
|
addsymref(sym);
|
|
pl.addsym(sl_call,sym);
|
|
end;
|
|
else
|
|
begin
|
|
Message1(parser_e_illegal_field_or_method,orgpattern);
|
|
result:=false;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Message1(parser_e_illegal_field_or_method,orgpattern);
|
|
result:=false;
|
|
end;
|
|
consume(_ID);
|
|
repeat
|
|
case token of
|
|
_ID,
|
|
_SEMICOLON :
|
|
begin
|
|
break;
|
|
end;
|
|
_POINT :
|
|
begin
|
|
consume(_POINT);
|
|
if assigned(def) then
|
|
begin
|
|
st:=def.GetSymtable(gs_record);
|
|
if assigned(st) then
|
|
begin
|
|
sym:=tsym(st.Find(pattern));
|
|
if assigned(sym) then
|
|
begin
|
|
pl.addsym(sl_subscript,sym);
|
|
case sym.typ of
|
|
fieldvarsym :
|
|
def:=tfieldvarsym(sym).vardef;
|
|
else
|
|
begin
|
|
Message1(sym_e_illegal_field,orgpattern);
|
|
result:=false;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Message1(sym_e_illegal_field,orgpattern);
|
|
result:=false;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Message(parser_e_invalid_qualifier);
|
|
result:=false;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Message(parser_e_invalid_qualifier);
|
|
result:=false;
|
|
end;
|
|
consume(_ID);
|
|
end;
|
|
_LECKKLAMMER :
|
|
begin
|
|
consume(_LECKKLAMMER);
|
|
repeat
|
|
if def.typ=arraydef then
|
|
begin
|
|
idx:=0;
|
|
p:=comp_expr(true);
|
|
if (not codegenerror) then
|
|
begin
|
|
if (p.nodetype=ordconstn) then
|
|
begin
|
|
if compare_defs(p.resultdef,tarraydef(def).rangedef,nothingn)>=te_equal then
|
|
idx:=tordconstnode(p).value
|
|
else
|
|
IncompatibleTypes(p.resultdef,tarraydef(def).rangedef);
|
|
end
|
|
else
|
|
Message(type_e_ordinal_expr_expected)
|
|
end;
|
|
p.free;
|
|
pl.addconst(sl_vec,idx,p.resultdef);
|
|
def:=tarraydef(def).elementdef;
|
|
end
|
|
else
|
|
begin
|
|
Message(parser_e_invalid_qualifier);
|
|
result:=false;
|
|
end;
|
|
until not try_to_consume(_COMMA);
|
|
consume(_RECKKLAMMER);
|
|
end;
|
|
else
|
|
begin
|
|
Message(parser_e_ill_property_access_sym);
|
|
result:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
until false;
|
|
end
|
|
else
|
|
begin
|
|
Message(parser_e_ill_property_access_sym);
|
|
result:=false;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
sym : tsym;
|
|
p : tpropertysym;
|
|
overriden : tsym;
|
|
varspez : tvarspez;
|
|
hdef : tdef;
|
|
arraytype : tdef;
|
|
def : tdef;
|
|
pt : tnode;
|
|
sc : TFPObjectList;
|
|
paranr : word;
|
|
i : longint;
|
|
ImplIntf : TImplementedInterface;
|
|
found : boolean;
|
|
hreadparavs,
|
|
hparavs : tparavarsym;
|
|
storedprocdef,
|
|
readprocdef,
|
|
writeprocdef : tprocvardef;
|
|
begin
|
|
{ Generate temp procvardefs to search for matching read/write
|
|
procedures. the readprocdef will store all definitions }
|
|
paranr:=0;
|
|
readprocdef:=tprocvardef.create(normal_function_level);
|
|
writeprocdef:=tprocvardef.create(normal_function_level);
|
|
storedprocdef:=tprocvardef.create(normal_function_level);
|
|
|
|
{ make it method pointers }
|
|
if assigned(aclass) then
|
|
begin
|
|
include(readprocdef.procoptions,po_methodpointer);
|
|
include(writeprocdef.procoptions,po_methodpointer);
|
|
include(storedprocdef.procoptions,po_methodpointer);
|
|
end;
|
|
|
|
{ method for stored must return boolean }
|
|
storedprocdef.returndef:=booltype;
|
|
|
|
if token<>_ID then
|
|
begin
|
|
consume(_ID);
|
|
consume(_SEMICOLON);
|
|
exit;
|
|
end;
|
|
{ Generate propertysym and insert in symtablestack }
|
|
p:=tpropertysym.create(orgpattern);
|
|
symtablestack.top.insert(p);
|
|
consume(_ID);
|
|
{ property parameters ? }
|
|
if try_to_consume(_LECKKLAMMER) then
|
|
begin
|
|
if (sp_published in current_object_option) and
|
|
not (m_delphi in current_settings.modeswitches) then
|
|
Message(parser_e_cant_publish_that_property);
|
|
{ create a list of the parameters }
|
|
symtablestack.push(readprocdef.parast);
|
|
sc:=TFPObjectList.create(false);
|
|
inc(testcurobject);
|
|
repeat
|
|
if try_to_consume(_VAR) then
|
|
varspez:=vs_var
|
|
else if try_to_consume(_CONST) then
|
|
varspez:=vs_const
|
|
else if (m_out in current_settings.modeswitches) and try_to_consume(_OUT) then
|
|
varspez:=vs_out
|
|
else
|
|
varspez:=vs_value;
|
|
sc.clear;
|
|
repeat
|
|
inc(paranr);
|
|
hreadparavs:=tparavarsym.create(orgpattern,10*paranr,varspez,generrordef,[]);
|
|
readprocdef.parast.insert(hreadparavs);
|
|
sc.add(hreadparavs);
|
|
consume(_ID);
|
|
until not try_to_consume(_COMMA);
|
|
if try_to_consume(_COLON) then
|
|
begin
|
|
if try_to_consume(_ARRAY) then
|
|
begin
|
|
consume(_OF);
|
|
{ define range and type of range }
|
|
hdef:=tarraydef.create(0,-1,s32inttype);
|
|
{ define field type }
|
|
single_type(arraytype,false);
|
|
tarraydef(hdef).elementdef:=arraytype;
|
|
end
|
|
else
|
|
single_type(hdef,false);
|
|
end
|
|
else
|
|
hdef:=cformaltype;
|
|
for i:=0 to sc.count-1 do
|
|
begin
|
|
hreadparavs:=tparavarsym(sc[i]);
|
|
hreadparavs.vardef:=hdef;
|
|
{ also update the writeprocdef }
|
|
hparavs:=tparavarsym.create(hreadparavs.realname,hreadparavs.paranr,vs_value,hdef,[]);
|
|
writeprocdef.parast.insert(hparavs);
|
|
end;
|
|
until not try_to_consume(_SEMICOLON);
|
|
sc.free;
|
|
dec(testcurobject);
|
|
symtablestack.pop(readprocdef.parast);
|
|
consume(_RECKKLAMMER);
|
|
|
|
{ the parser need to know if a property has parameters, the
|
|
index parameter doesn't count (PFV) }
|
|
if paranr>0 then
|
|
include(p.propoptions,ppo_hasparameters);
|
|
end;
|
|
{ overriden property ? }
|
|
{ force property interface
|
|
there is a property parameter
|
|
a global property }
|
|
if (token=_COLON) or (paranr>0) or (aclass=nil) then
|
|
begin
|
|
consume(_COLON);
|
|
single_type(p.propdef,false);
|
|
if (idtoken=_INDEX) then
|
|
begin
|
|
consume(_INDEX);
|
|
pt:=comp_expr(true);
|
|
{ Only allow enum and integer indexes. Convert all integer
|
|
values to s32int to be compatible with delphi, because the
|
|
procedure matching requires equal parameters }
|
|
if is_constnode(pt) and
|
|
is_ordinal(pt.resultdef)
|
|
{$ifndef cpu64bit}
|
|
and (not is_64bitint(pt.resultdef))
|
|
{$endif cpu64bit}
|
|
then
|
|
begin
|
|
if is_integer(pt.resultdef) then
|
|
inserttypeconv_internal(pt,s32inttype);
|
|
p.index:=tordconstnode(pt).value;
|
|
end
|
|
else
|
|
begin
|
|
Message(parser_e_invalid_property_index_value);
|
|
p.index:=0;
|
|
end;
|
|
p.indexdef:=pt.resultdef;
|
|
include(p.propoptions,ppo_indexed);
|
|
{ concat a longint to the para templates }
|
|
inc(paranr);
|
|
hparavs:=tparavarsym.create('$index',10*paranr,vs_value,p.indexdef,[]);
|
|
readprocdef.parast.insert(hparavs);
|
|
hparavs:=tparavarsym.create('$index',10*paranr,vs_value,p.indexdef,[]);
|
|
writeprocdef.parast.insert(hparavs);
|
|
hparavs:=tparavarsym.create('$index',10*paranr,vs_value,p.indexdef,[]);
|
|
storedprocdef.parast.insert(hparavs);
|
|
pt.free;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
{ do an property override }
|
|
overriden:=search_class_member(aclass.childof,p.name);
|
|
if assigned(overriden) and
|
|
(overriden.typ=propertysym) and
|
|
not(is_dispinterface(aclass)) then
|
|
begin
|
|
p.overridenpropsym:=tpropertysym(overriden);
|
|
{ inherit all type related entries }
|
|
p.indexdef:=tpropertysym(overriden).indexdef;
|
|
p.propdef:=tpropertysym(overriden).propdef;
|
|
p.index:=tpropertysym(overriden).index;
|
|
p.default:=tpropertysym(overriden).default;
|
|
p.propoptions:=tpropertysym(overriden).propoptions;
|
|
end
|
|
else
|
|
begin
|
|
p.propdef:=generrordef;
|
|
message(parser_e_no_property_found_to_override);
|
|
end;
|
|
end;
|
|
if ((sp_published in current_object_option) or is_dispinterface(aclass)) and
|
|
not(p.propdef.is_publishable) then
|
|
Message(parser_e_cant_publish_that_property);
|
|
|
|
if not(is_dispinterface(aclass)) then
|
|
begin
|
|
if try_to_consume(_READ) then
|
|
begin
|
|
p.propaccesslist[palt_read].clear;
|
|
if parse_symlist(p.propaccesslist[palt_read],def) then
|
|
begin
|
|
sym:=p.propaccesslist[palt_read].firstsym^.sym;
|
|
case sym.typ of
|
|
procsym :
|
|
begin
|
|
{ read is function returning the type of the property }
|
|
readprocdef.returndef:=p.propdef;
|
|
{ Insert hidden parameters }
|
|
handle_calling_convention(readprocdef);
|
|
{ search procdefs matching readprocdef }
|
|
{ we ignore hidden stuff here because the property access symbol might have
|
|
non default calling conventions which might change the hidden stuff;
|
|
see tw3216.pp (FK) }
|
|
p.propaccesslist[palt_read].procdef:=Tprocsym(sym).Find_procdef_bypara(readprocdef.paras,p.propdef,[cpo_allowdefaults,cpo_ignorehidden]);
|
|
if not assigned(p.propaccesslist[palt_read].procdef) then
|
|
Message(parser_e_ill_property_access_sym);
|
|
end;
|
|
fieldvarsym :
|
|
begin
|
|
if not assigned(def) then
|
|
internalerror(200310071);
|
|
if compare_defs(def,p.propdef,nothingn)>=te_equal then
|
|
begin
|
|
{ property parameters are allowed if this is
|
|
an indexed property, because the index is then
|
|
the parameter.
|
|
Note: In the help of Kylix it is written
|
|
that it isn't allowed, but the compiler accepts it (PFV) }
|
|
if (ppo_hasparameters in p.propoptions) then
|
|
Message(parser_e_ill_property_access_sym);
|
|
end
|
|
else
|
|
IncompatibleTypes(def,p.propdef);
|
|
end;
|
|
else
|
|
Message(parser_e_ill_property_access_sym);
|
|
end;
|
|
end;
|
|
end;
|
|
if try_to_consume(_WRITE) then
|
|
begin
|
|
p.propaccesslist[palt_write].clear;
|
|
if parse_symlist(p.propaccesslist[palt_write],def) then
|
|
begin
|
|
sym:=p.propaccesslist[palt_write].firstsym^.sym;
|
|
case sym.typ of
|
|
procsym :
|
|
begin
|
|
{ write is a procedure with an extra value parameter
|
|
of the of the property }
|
|
writeprocdef.returndef:=voidtype;
|
|
inc(paranr);
|
|
hparavs:=tparavarsym.create('$value',10*paranr,vs_value,p.propdef,[]);
|
|
writeprocdef.parast.insert(hparavs);
|
|
{ Insert hidden parameters }
|
|
handle_calling_convention(writeprocdef);
|
|
{ search procdefs matching writeprocdef }
|
|
p.propaccesslist[palt_write].procdef:=Tprocsym(sym).Find_procdef_bypara(writeprocdef.paras,writeprocdef.returndef,[cpo_allowdefaults]);
|
|
if not assigned(p.propaccesslist[palt_write].procdef) then
|
|
Message(parser_e_ill_property_access_sym);
|
|
end;
|
|
fieldvarsym :
|
|
begin
|
|
if not assigned(def) then
|
|
internalerror(200310072);
|
|
if compare_defs(def,p.propdef,nothingn)>=te_equal then
|
|
begin
|
|
{ property parameters are allowed if this is
|
|
an indexed property, because the index is then
|
|
the parameter.
|
|
Note: In the help of Kylix it is written
|
|
that it isn't allowed, but the compiler accepts it (PFV) }
|
|
if (ppo_hasparameters in p.propoptions) then
|
|
Message(parser_e_ill_property_access_sym);
|
|
end
|
|
else
|
|
IncompatibleTypes(def,p.propdef);
|
|
end;
|
|
else
|
|
Message(parser_e_ill_property_access_sym);
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if try_to_consume(_READONLY) then
|
|
begin
|
|
end
|
|
else if try_to_consume(_WRITEONLY) then
|
|
begin
|
|
end;
|
|
if try_to_consume(_DISPID) then
|
|
begin
|
|
pt:=comp_expr(true);
|
|
if is_constintnode(pt) then
|
|
// tprocdef(pd).extnumber:=tordconstnode(pt).value
|
|
else
|
|
Message(parser_e_dispid_must_be_ord_const);
|
|
pt.free;
|
|
end;
|
|
end;
|
|
|
|
if assigned(aclass) and not(is_dispinterface(aclass)) then
|
|
begin
|
|
{ ppo_stored is default on for not overriden properties }
|
|
if not assigned(p.overridenpropsym) then
|
|
include(p.propoptions,ppo_stored);
|
|
if try_to_consume(_STORED) then
|
|
begin
|
|
include(p.propoptions,ppo_stored);
|
|
p.propaccesslist[palt_stored].clear;
|
|
case token of
|
|
_ID:
|
|
begin
|
|
{ in the case that idtoken=_DEFAULT }
|
|
{ we have to do nothing except }
|
|
{ setting ppo_stored, it's the same }
|
|
{ as stored true }
|
|
if idtoken<>_DEFAULT then
|
|
begin
|
|
if parse_symlist(p.propaccesslist[palt_stored],def) then
|
|
begin
|
|
sym:=p.propaccesslist[palt_stored].firstsym^.sym;
|
|
case sym.typ of
|
|
procsym :
|
|
begin
|
|
{ Insert hidden parameters }
|
|
handle_calling_convention(storedprocdef);
|
|
p.propaccesslist[palt_stored].procdef:=Tprocsym(sym).Find_procdef_bypara(storedprocdef.paras,storedprocdef.returndef,[cpo_allowdefaults,cpo_ignorehidden]);
|
|
if not assigned(p.propaccesslist[palt_stored].procdef) then
|
|
message(parser_e_ill_property_storage_sym);
|
|
end;
|
|
fieldvarsym :
|
|
begin
|
|
if not assigned(def) then
|
|
internalerror(200310073);
|
|
if (ppo_hasparameters in p.propoptions) or
|
|
not(is_boolean(def)) then
|
|
Message(parser_e_stored_property_must_be_boolean);
|
|
end;
|
|
else
|
|
Message(parser_e_ill_property_access_sym);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
_FALSE:
|
|
begin
|
|
consume(_FALSE);
|
|
exclude(p.propoptions,ppo_stored);
|
|
end;
|
|
_TRUE:
|
|
begin
|
|
p.default:=longint($80000000);
|
|
consume(_TRUE);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
if try_to_consume(_DEFAULT) then
|
|
begin
|
|
if not(is_ordinal(p.propdef) or
|
|
{$ifndef cpu64bit}
|
|
is_64bitint(p.propdef) or
|
|
{$endif cpu64bit}
|
|
is_class(p.propdef) or
|
|
is_single(p.propdef) or
|
|
(p.propdef.typ in [classrefdef,pointerdef]) or
|
|
((p.propdef.typ=setdef) and
|
|
(tsetdef(p.propdef).settype=smallset))) or
|
|
((p.propdef.typ=arraydef) and
|
|
(ppo_indexed in p.propoptions)) or
|
|
(ppo_hasparameters in p.propoptions) then
|
|
begin
|
|
Message(parser_e_property_cant_have_a_default_value);
|
|
{ Error recovery }
|
|
pt:=comp_expr(true);
|
|
pt.free;
|
|
end
|
|
else
|
|
begin
|
|
{ Get the result of the default, the firstpass is
|
|
needed to support values like -1 }
|
|
pt:=comp_expr(true);
|
|
if (p.propdef.typ=setdef) and
|
|
(pt.nodetype=arrayconstructorn) then
|
|
begin
|
|
arrayconstructor_to_set(pt);
|
|
do_typecheckpass(pt);
|
|
end;
|
|
inserttypeconv(pt,p.propdef);
|
|
if not(is_constnode(pt)) then
|
|
Message(parser_e_property_default_value_must_const);
|
|
{ Set default value }
|
|
case pt.nodetype of
|
|
setconstn :
|
|
p.default:=plongint(tsetconstnode(pt).value_set)^;
|
|
ordconstn :
|
|
p.default:=longint(tordconstnode(pt).value);
|
|
niln :
|
|
p.default:=0;
|
|
realconstn:
|
|
p.default:=longint(single(trealconstnode(pt).value_real));
|
|
end;
|
|
pt.free;
|
|
end;
|
|
end
|
|
else if try_to_consume(_NODEFAULT) then
|
|
begin
|
|
p.default:=longint($80000000);
|
|
end;
|
|
{ Parse possible "implements" keyword }
|
|
if try_to_consume(_IMPLEMENTS) then
|
|
begin
|
|
consume(_ID);
|
|
if not is_interface(p.propdef) then
|
|
begin
|
|
Comment(V_Error,'Implements property must have interface type');
|
|
end;
|
|
if pattern <> p.propdef.mangledparaname() then
|
|
begin
|
|
Comment(V_Error,'Implements-property must implement interface of correct type');
|
|
end;
|
|
found:=false;
|
|
for i:=0 to aclass.ImplementedInterfaces.Count-1 do
|
|
begin
|
|
ImplIntf:=TImplementedInterface(aclass.ImplementedInterfaces[i]);
|
|
if ImplIntf.IntfDef.Objname^=pattern then
|
|
begin
|
|
found:=true;
|
|
break;
|
|
end;
|
|
end;
|
|
if found then
|
|
begin
|
|
ImplIntf.IntfDef.iitype := etFieldValue;
|
|
ImplIntf.IntfDef.iioffset := tfieldvarsym(p.propaccesslist[palt_read].firstsym^.sym).fieldoffset;
|
|
end
|
|
else
|
|
Comment(V_Error,'Implements-property used on unimplemented interface');
|
|
end;
|
|
|
|
{ remove temporary procvardefs }
|
|
readprocdef.owner.deletedef(readprocdef);
|
|
writeprocdef.owner.deletedef(writeprocdef);
|
|
|
|
result:=p;
|
|
end;
|
|
|
|
|
|
function maybe_parse_proc_directives(def:tdef):boolean;
|
|
var
|
|
newtype : ttypesym;
|
|
begin
|
|
result:=false;
|
|
{ Process procvar directives before = and ; }
|
|
if (def.typ=procvardef) and
|
|
(def.typesym=nil) and
|
|
check_proc_directive(true) then
|
|
begin
|
|
newtype:=ttypesym.create('unnamed',def);
|
|
parse_var_proc_directives(tsym(newtype));
|
|
newtype.typedef:=nil;
|
|
def.typesym:=nil;
|
|
newtype.free;
|
|
result:=true;
|
|
end;
|
|
end;
|
|
|
|
|
|
const
|
|
variantrecordlevel : longint = 0;
|
|
|
|
procedure read_var_decls(options:Tvar_dec_options);
|
|
|
|
procedure read_default_value(sc : TFPObjectList);
|
|
var
|
|
vs : tabstractnormalvarsym;
|
|
tcsym : tstaticvarsym;
|
|
begin
|
|
vs:=tabstractnormalvarsym(sc[0]);
|
|
if sc.count>1 then
|
|
Message(parser_e_initialized_only_one_var);
|
|
if vo_is_thread_var in vs.varoptions then
|
|
Message(parser_e_initialized_not_for_threadvar);
|
|
consume(_EQUAL);
|
|
case vs.typ of
|
|
localvarsym :
|
|
begin
|
|
tcsym:=tstaticvarsym.create('$default'+vs.realname,vs_const,vs.vardef,[]);
|
|
include(tcsym.symoptions,sp_internal);
|
|
vs.defaultconstsym:=tcsym;
|
|
symtablestack.top.insert(tcsym);
|
|
read_typed_const(current_asmdata.asmlists[al_typedconsts],tcsym);
|
|
end;
|
|
staticvarsym :
|
|
begin
|
|
read_typed_const(current_asmdata.asmlists[al_typedconsts],tstaticvarsym(vs));
|
|
end;
|
|
else
|
|
internalerror(200611051);
|
|
end;
|
|
vs.varstate:=vs_initialised;
|
|
end;
|
|
|
|
procedure read_gpc_name(sc : TFPObjectList);
|
|
var
|
|
vs : tabstractnormalvarsym;
|
|
C_Name : string;
|
|
begin
|
|
consume(_ID);
|
|
C_Name:=get_stringconst;
|
|
vs:=tabstractnormalvarsym(sc[0]);
|
|
if sc.count>1 then
|
|
Message(parser_e_absolute_only_one_var);
|
|
if vs.typ=staticvarsym then
|
|
begin
|
|
tstaticvarsym(vs).set_mangledname(C_Name);
|
|
include(vs.varoptions,vo_is_external);
|
|
end
|
|
else
|
|
Message(parser_e_no_local_var_external);
|
|
end;
|
|
|
|
procedure read_absolute(sc : TFPObjectList);
|
|
var
|
|
vs : tabstractvarsym;
|
|
abssym : tabsolutevarsym;
|
|
pt,hp : tnode;
|
|
st : tsymtable;
|
|
begin
|
|
abssym:=nil;
|
|
{ only allowed for one var }
|
|
vs:=tabstractvarsym(sc[0]);
|
|
if sc.count>1 then
|
|
Message(parser_e_absolute_only_one_var);
|
|
{ parse the rest }
|
|
pt:=expr;
|
|
{ check allowed absolute types }
|
|
if (pt.nodetype=stringconstn) or
|
|
(is_constcharnode(pt)) then
|
|
begin
|
|
abssym:=tabsolutevarsym.create(vs.realname,vs.vardef);
|
|
abssym.fileinfo:=vs.fileinfo;
|
|
if pt.nodetype=stringconstn then
|
|
abssym.asmname:=stringdup(strpas(tstringconstnode(pt).value_str))
|
|
else
|
|
abssym.asmname:=stringdup(chr(tordconstnode(pt).value));
|
|
consume(token);
|
|
abssym.abstyp:=toasm;
|
|
end
|
|
{ address }
|
|
else if is_constintnode(pt) then
|
|
begin
|
|
abssym:=tabsolutevarsym.create(vs.realname,vs.vardef);
|
|
abssym.fileinfo:=vs.fileinfo;
|
|
abssym.abstyp:=toaddr;
|
|
abssym.addroffset:=tordconstnode(pt).value;
|
|
{$ifdef i386}
|
|
abssym.absseg:=false;
|
|
if (target_info.system in [system_i386_go32v2,system_i386_watcom]) and
|
|
try_to_consume(_COLON) then
|
|
begin
|
|
pt.free;
|
|
pt:=expr;
|
|
if is_constintnode(pt) then
|
|
begin
|
|
abssym.addroffset:=abssym.addroffset shl 4+tordconstnode(pt).value;
|
|
abssym.absseg:=true;
|
|
end
|
|
else
|
|
Message(type_e_ordinal_expr_expected);
|
|
end;
|
|
{$endif i386}
|
|
end
|
|
{ variable }
|
|
else
|
|
begin
|
|
{ remove subscriptn before checking for loadn }
|
|
hp:=pt;
|
|
while (hp.nodetype in [subscriptn,typeconvn,vecn]) do
|
|
hp:=tunarynode(hp).left;
|
|
if (hp.nodetype=loadn) then
|
|
begin
|
|
{ we should check the result type of loadn }
|
|
if not (tloadnode(hp).symtableentry.typ in [fieldvarsym,staticvarsym,localvarsym,paravarsym]) then
|
|
Message(parser_e_absolute_only_to_var_or_const);
|
|
abssym:=tabsolutevarsym.create(vs.realname,vs.vardef);
|
|
abssym.fileinfo:=vs.fileinfo;
|
|
abssym.abstyp:=tovar;
|
|
abssym.ref:=node_to_propaccesslist(pt);
|
|
end
|
|
else
|
|
Message(parser_e_absolute_only_to_var_or_const);
|
|
end;
|
|
pt.free;
|
|
{ replace old varsym with the new absolutevarsym }
|
|
if assigned(abssym) then
|
|
begin
|
|
st:=vs.owner;
|
|
vs.owner.Delete(vs);
|
|
st.insert(abssym);
|
|
sc[0]:=abssym;
|
|
end;
|
|
end;
|
|
|
|
procedure read_public_and_external(sc:TFPObjectList);
|
|
var
|
|
vs : tabstractvarsym;
|
|
is_dll,
|
|
is_cdecl,
|
|
is_external_var,
|
|
is_public_var : boolean;
|
|
dll_name,
|
|
C_name : string;
|
|
begin
|
|
{ only allowed for one var }
|
|
vs:=tabstractvarsym(sc[0]);
|
|
if sc.count>1 then
|
|
Message(parser_e_absolute_only_one_var);
|
|
{ only allow external and public on global symbols }
|
|
if vs.typ<>staticvarsym then
|
|
begin
|
|
Message(parser_e_no_local_var_external);
|
|
exit;
|
|
end;
|
|
|
|
{ defaults }
|
|
is_dll:=false;
|
|
is_cdecl:=false;
|
|
is_external_var:=false;
|
|
is_public_var:=false;
|
|
C_name:=vs.realname;
|
|
|
|
{ macpas specific handling due to some switches}
|
|
if (m_mac in current_settings.modeswitches) then
|
|
begin
|
|
if (cs_external_var in current_settings.localswitches) then
|
|
begin {The effect of this is the same as if cvar; external; has been given as directives.}
|
|
is_cdecl:=true;
|
|
is_external_var:=true;
|
|
end
|
|
else if (cs_externally_visible in current_settings.localswitches) then
|
|
begin {The effect of this is the same as if cvar has been given as directives and it's made public.}
|
|
is_cdecl:=true;
|
|
is_public_var:=true;
|
|
end;
|
|
end;
|
|
|
|
{ cdecl }
|
|
if try_to_consume(_CVAR) then
|
|
begin
|
|
consume(_SEMICOLON);
|
|
is_cdecl:=true;
|
|
end;
|
|
|
|
{ external }
|
|
if try_to_consume(_EXTERNAL) then
|
|
begin
|
|
is_external_var:=true;
|
|
if not is_cdecl then
|
|
begin
|
|
if idtoken<>_NAME then
|
|
begin
|
|
is_dll:=true;
|
|
dll_name:=get_stringconst;
|
|
if ExtractFileExt(dll_name)='' then
|
|
dll_name:=ChangeFileExt(dll_name,target_info.sharedlibext);
|
|
end;
|
|
if try_to_consume(_NAME) then
|
|
C_name:=get_stringconst;
|
|
end;
|
|
consume(_SEMICOLON);
|
|
end;
|
|
|
|
{ export or public }
|
|
if idtoken in [_EXPORT,_PUBLIC] then
|
|
begin
|
|
consume(_ID);
|
|
if is_external_var then
|
|
Message(parser_e_not_external_and_export)
|
|
else
|
|
is_public_var:=true;
|
|
if try_to_consume(_NAME) then
|
|
C_name:=get_stringconst;
|
|
consume(_SEMICOLON);
|
|
end;
|
|
|
|
{ Windows uses an indirect reference using import tables }
|
|
if is_dll and
|
|
(target_info.system in system_all_windows) then
|
|
include(vs.varoptions,vo_is_dll_var);
|
|
|
|
{ Add C _ prefix }
|
|
if is_cdecl or
|
|
(
|
|
is_dll and
|
|
(target_info.system in [system_powerpc_darwin,system_i386_darwin])
|
|
) then
|
|
C_Name := target_info.Cprefix+C_Name;
|
|
|
|
if is_public_var then
|
|
begin
|
|
include(vs.varoptions,vo_is_public);
|
|
vs.varregable := vr_none;
|
|
{ mark as referenced }
|
|
inc(vs.refs);
|
|
end;
|
|
|
|
{ now we can insert it in the import lib if its a dll, or
|
|
add it to the externals }
|
|
if is_external_var then
|
|
begin
|
|
include(vs.varoptions,vo_is_external);
|
|
vs.varregable := vr_none;
|
|
if is_dll then
|
|
current_module.AddExternalImport(dll_name,C_Name,0,true)
|
|
else
|
|
if tf_has_dllscanner in target_info.flags then
|
|
current_module.dllscannerinputlist.Add(vs.mangledname,vs);
|
|
end;
|
|
|
|
{ Set the assembler name }
|
|
tstaticvarsym(vs).set_mangledname(C_Name);
|
|
end;
|
|
|
|
var
|
|
sc : TFPObjectList;
|
|
vs : tabstractvarsym;
|
|
hdef : tdef;
|
|
i : longint;
|
|
semicoloneaten,
|
|
hasdefaultvalue : boolean;
|
|
old_current_object_option : tsymoptions;
|
|
hintsymoptions : tsymoptions;
|
|
old_block_type : tblock_type;
|
|
begin
|
|
old_current_object_option:=current_object_option;
|
|
{ all variables are public if not in a object declaration }
|
|
current_object_option:=[sp_public];
|
|
old_block_type:=block_type;
|
|
block_type:=bt_type;
|
|
{ Force an expected ID error message }
|
|
if not (token in [_ID,_CASE,_END]) then
|
|
consume(_ID);
|
|
{ read vars }
|
|
sc:=TFPObjectList.create(false);
|
|
while (token=_ID) do
|
|
begin
|
|
semicoloneaten:=false;
|
|
hasdefaultvalue:=false;
|
|
sc.clear;
|
|
repeat
|
|
if (token = _ID) then
|
|
begin
|
|
case symtablestack.top.symtabletype of
|
|
localsymtable :
|
|
vs:=tlocalvarsym.create(orgpattern,vs_value,generrordef,[]);
|
|
staticsymtable,
|
|
globalsymtable :
|
|
begin
|
|
vs:=tstaticvarsym.create(orgpattern,vs_value,generrordef,[]);
|
|
if vd_threadvar in options then
|
|
include(vs.varoptions,vo_is_thread_var);
|
|
end;
|
|
else
|
|
internalerror(200411064);
|
|
end;
|
|
sc.add(vs);
|
|
symtablestack.top.insert(vs);
|
|
end;
|
|
consume(_ID);
|
|
until not try_to_consume(_COMMA);
|
|
consume(_COLON);
|
|
|
|
if (m_gpc in current_settings.modeswitches) and
|
|
(token=_ID) and
|
|
(orgpattern='__asmname__') then
|
|
read_gpc_name(sc);
|
|
|
|
{ read variable type def }
|
|
read_anon_type(hdef,false);
|
|
for i:=0 to sc.count-1 do
|
|
begin
|
|
vs:=tabstractvarsym(sc[i]);
|
|
vs.vardef:=hdef;
|
|
end;
|
|
|
|
{ Process procvar directives }
|
|
if maybe_parse_proc_directives(hdef) then
|
|
semicoloneaten:=true;
|
|
|
|
{ check for absolute }
|
|
if try_to_consume(_ABSOLUTE) then
|
|
read_absolute(sc);
|
|
|
|
{ try to parse the hint directives }
|
|
hintsymoptions:=[];
|
|
try_consume_hintdirective(hintsymoptions);
|
|
for i:=0 to sc.count-1 do
|
|
begin
|
|
vs:=tabstractvarsym(sc[i]);
|
|
vs.symoptions := vs.symoptions + hintsymoptions;
|
|
end;
|
|
|
|
{ Handling of Delphi typed const = initialized vars }
|
|
if (token=_EQUAL) and
|
|
not(m_tp7 in current_settings.modeswitches) and
|
|
(symtablestack.top.symtabletype<>parasymtable) then
|
|
begin
|
|
{ Add calling convention for procvar }
|
|
if (hdef.typ=procvardef) and
|
|
(hdef.typesym=nil) then
|
|
handle_calling_convention(tprocvardef(hdef));
|
|
read_default_value(sc);
|
|
consume(_SEMICOLON);
|
|
hasdefaultvalue:=true;
|
|
end
|
|
else
|
|
begin
|
|
if not(semicoloneaten) then
|
|
consume(_SEMICOLON);
|
|
end;
|
|
|
|
{ Support calling convention for procvars after semicolon }
|
|
if not(hasdefaultvalue) and
|
|
(hdef.typ=procvardef) and
|
|
(hdef.typesym=nil) then
|
|
begin
|
|
{ Parse procvar directives after ; }
|
|
maybe_parse_proc_directives(hdef);
|
|
{ Add calling convention for procvar }
|
|
handle_calling_convention(tprocvardef(hdef));
|
|
{ Handling of Delphi typed const = initialized vars }
|
|
if (token=_EQUAL) and
|
|
not(m_tp7 in current_settings.modeswitches) and
|
|
(symtablestack.top.symtabletype<>parasymtable) then
|
|
begin
|
|
read_default_value(sc);
|
|
consume(_SEMICOLON);
|
|
hasdefaultvalue:=true;
|
|
end;
|
|
end;
|
|
|
|
{ Check for EXTERNAL etc directives or, in macpas, if cs_external_var is set}
|
|
if (not hasdefaultvalue) and
|
|
(
|
|
(
|
|
(idtoken in [_EXPORT,_EXTERNAL,_PUBLIC,_CVAR]) and
|
|
(m_cvar_support in current_settings.modeswitches)
|
|
) or
|
|
(
|
|
(m_mac in current_settings.modeswitches) and
|
|
(
|
|
(cs_external_var in current_settings.localswitches) or
|
|
(cs_externally_visible in current_settings.localswitches)
|
|
)
|
|
)
|
|
) then
|
|
read_public_and_external(sc);
|
|
|
|
{ allocate normal variable (non-external and non-typed-const) staticvarsyms }
|
|
for i:=0 to sc.count-1 do
|
|
begin
|
|
vs:=tabstractvarsym(sc[i]);
|
|
if (vs.typ=staticvarsym) and
|
|
not(vo_is_typed_const in vs.varoptions) and
|
|
not(vo_is_external in vs.varoptions) then
|
|
insertbssdata(tstaticvarsym(vs));
|
|
end;
|
|
end;
|
|
block_type:=old_block_type;
|
|
current_object_option:=old_current_object_option;
|
|
{ free the list }
|
|
sc.free;
|
|
end;
|
|
|
|
|
|
procedure read_record_fields(options:Tvar_dec_options);
|
|
var
|
|
sc : TFPObjectList;
|
|
i : longint;
|
|
old_block_type : tblock_type;
|
|
old_current_object_option : tsymoptions;
|
|
hs,sorg : string;
|
|
hdef,casetype : tdef;
|
|
{ maxsize contains the max. size of a variant }
|
|
{ startvarrec contains the start of the variant part of a record }
|
|
maxsize, startvarrecsize : longint;
|
|
usedalign,
|
|
maxalignment,startvarrecalign,
|
|
maxpadalign, startpadalign: shortint;
|
|
pt : tnode;
|
|
fieldvs : tfieldvarsym;
|
|
hstaticvs : tstaticvarsym;
|
|
vs : tabstractvarsym;
|
|
srsym : tsym;
|
|
srsymtable : TSymtable;
|
|
recst : tabstractrecordsymtable;
|
|
unionsymtable : trecordsymtable;
|
|
offset : longint;
|
|
uniondef : trecorddef;
|
|
hintsymoptions : tsymoptions;
|
|
semicoloneaten: boolean;
|
|
{$ifdef powerpc}
|
|
tempdef: tdef;
|
|
is_first_field: boolean;
|
|
{$endif powerpc}
|
|
begin
|
|
recst:=tabstractrecordsymtable(symtablestack.top);
|
|
{$ifdef powerpc}
|
|
is_first_field := true;
|
|
{$endif powerpc}
|
|
old_current_object_option:=current_object_option;
|
|
{ all variables are public if not in a object declaration }
|
|
if not(vd_object in options) then
|
|
current_object_option:=[sp_public];
|
|
old_block_type:=block_type;
|
|
block_type:=bt_type;
|
|
{ Force an expected ID error message }
|
|
if not (token in [_ID,_CASE,_END]) then
|
|
consume(_ID);
|
|
{ read vars }
|
|
sc:=TFPObjectList.create(false);
|
|
while (token=_ID) and
|
|
not((vd_object in options) and
|
|
(idtoken in [_PUBLIC,_PRIVATE,_PUBLISHED,_PROTECTED,_STRICT])) do
|
|
begin
|
|
sorg:=orgpattern;
|
|
semicoloneaten:=false;
|
|
sc.clear;
|
|
repeat
|
|
if try_to_consume(_ID) then
|
|
begin
|
|
vs:=tfieldvarsym.create(orgpattern,vs_value,generrordef,[]);
|
|
sc.add(vs);
|
|
recst.insert(vs);
|
|
end;
|
|
until not try_to_consume(_COMMA);
|
|
consume(_COLON);
|
|
|
|
{ Don't search in the recordsymtable for types }
|
|
if ([df_generic,df_specialization]*tdef(recst.defowner).defoptions=[]) then
|
|
symtablestack.pop(recst);
|
|
read_anon_type(hdef,false);
|
|
if ([df_generic,df_specialization]*tdef(recst.defowner).defoptions=[]) then
|
|
symtablestack.push(recst);
|
|
|
|
{ Process procvar directives }
|
|
if maybe_parse_proc_directives(hdef) then
|
|
semicoloneaten:=true;
|
|
|
|
{$ifdef powerpc}
|
|
{ from gcc/gcc/config/rs6000/rs6000.h:
|
|
/* APPLE LOCAL begin Macintosh alignment 2002-1-22 ff */
|
|
/* Return the alignment of a struct based on the Macintosh PowerPC
|
|
alignment rules. In general the alignment of a struct is
|
|
determined by the greatest alignment of its elements. However, the
|
|
PowerPC rules cause the alignment of a struct to peg at word
|
|
alignment except when the first field has greater than word
|
|
(32-bit) alignment, in which case the alignment is determined by
|
|
the alignment of the first field. */
|
|
}
|
|
if (target_info.system in [system_powerpc_darwin, system_powerpc_macos]) and
|
|
is_first_field and
|
|
(symtablestack.top.symtabletype = recordsymtable) and
|
|
(trecordsymtable(symtablestack.top).usefieldalignment = -1) then
|
|
begin
|
|
tempdef := hdef;
|
|
while tempdef.typ = arraydef do
|
|
tempdef := tarraydef(tempdef).elementdef;
|
|
if tempdef.typ <> recorddef then
|
|
maxpadalign := tempdef.alignment
|
|
else
|
|
maxpadalign := trecorddef(tempdef).padalignment;
|
|
|
|
if (maxpadalign > 4) and
|
|
(maxpadalign > trecordsymtable(symtablestack.top).padalignment) then
|
|
trecordsymtable(symtablestack.top).padalignment := maxpadalign;
|
|
is_first_field := false;
|
|
end;
|
|
{$endif powerpc}
|
|
|
|
{ types that use init/final are not allowed in variant parts, but
|
|
classes are allowed }
|
|
if (variantrecordlevel>0) and
|
|
(hdef.needs_inittable and not is_class(hdef)) then
|
|
Message(parser_e_cant_use_inittable_here);
|
|
|
|
{ try to parse the hint directives }
|
|
hintsymoptions:=[];
|
|
try_consume_hintdirective(hintsymoptions);
|
|
|
|
{ update variable type and hints }
|
|
for i:=0 to sc.count-1 do
|
|
begin
|
|
fieldvs:=tfieldvarsym(sc[i]);
|
|
fieldvs.vardef:=hdef;
|
|
{ insert any additional hint directives }
|
|
fieldvs.symoptions := fieldvs.symoptions + hintsymoptions;
|
|
end;
|
|
|
|
{ Records and objects can't have default values }
|
|
{ for a record there doesn't need to be a ; before the END or ) }
|
|
if not(token in [_END,_RKLAMMER]) and
|
|
not(semicoloneaten) then
|
|
consume(_SEMICOLON);
|
|
|
|
{ Parse procvar directives after ; }
|
|
maybe_parse_proc_directives(hdef);
|
|
|
|
{ Add calling convention for procvar }
|
|
if (hdef.typ=procvardef) and
|
|
(hdef.typesym=nil) then
|
|
handle_calling_convention(tprocvardef(hdef));
|
|
|
|
{ Check for STATIC directive }
|
|
if (vd_object in options) and
|
|
(cs_static_keyword in current_settings.moduleswitches) and
|
|
(try_to_consume(_STATIC)) then
|
|
begin
|
|
{ add static flag and staticvarsyms }
|
|
for i:=0 to sc.count-1 do
|
|
begin
|
|
fieldvs:=tfieldvarsym(sc[i]);
|
|
include(fieldvs.symoptions,sp_static);
|
|
hstaticvs:=tstaticvarsym.create('$'+lower(symtablestack.top.name^)+'_'+fieldvs.name,vs_value,hdef,[]);
|
|
recst.defowner.owner.insert(hstaticvs);
|
|
insertbssdata(hstaticvs);
|
|
end;
|
|
consume(_SEMICOLON);
|
|
end;
|
|
|
|
if (sp_published in current_object_option) and
|
|
not(is_class(hdef)) then
|
|
begin
|
|
Message(parser_e_cant_publish_that);
|
|
exclude(current_object_option,sp_published);
|
|
{ recover by changing access type to public }
|
|
for i:=0 to sc.count-1 do
|
|
begin
|
|
fieldvs:=tfieldvarsym(sc[i]);
|
|
exclude(fieldvs.symoptions,sp_published);
|
|
include(fieldvs.symoptions,sp_public);
|
|
end;
|
|
end
|
|
else
|
|
if (sp_published in current_object_option) and
|
|
not(oo_can_have_published in tobjectdef(hdef).objectoptions) then
|
|
begin
|
|
Message(parser_e_only_publishable_classes_can__be_published);
|
|
exclude(current_object_option,sp_published);
|
|
end;
|
|
|
|
{ Generate field in the recordsymtable }
|
|
for i:=0 to sc.count-1 do
|
|
begin
|
|
fieldvs:=tfieldvarsym(sc[i]);
|
|
{ static data fields are already inserted in the globalsymtable }
|
|
if not(sp_static in current_object_option) then
|
|
recst.addfield(fieldvs);
|
|
end;
|
|
|
|
{ restore current_object_option, it can be changed for
|
|
publishing or static }
|
|
current_object_option:=old_current_object_option;
|
|
end;
|
|
|
|
{ Check for Case }
|
|
if (vd_record in options) and
|
|
try_to_consume(_CASE) then
|
|
begin
|
|
maxsize:=0;
|
|
maxalignment:=0;
|
|
maxpadalign:=0;
|
|
{ including a field declaration? }
|
|
fieldvs:=nil;
|
|
sorg:=orgpattern;
|
|
hs:=pattern;
|
|
searchsym(hs,srsym,srsymtable);
|
|
if not(assigned(srsym) and (srsym.typ in [typesym,unitsym])) then
|
|
begin
|
|
consume(_ID);
|
|
consume(_COLON);
|
|
fieldvs:=tfieldvarsym.create(sorg,vs_value,generrordef,[]);
|
|
symtablestack.top.insert(fieldvs);
|
|
end;
|
|
read_anon_type(casetype,true);
|
|
if assigned(fieldvs) then
|
|
begin
|
|
fieldvs.vardef:=casetype;
|
|
recst.addfield(fieldvs);
|
|
end;
|
|
if not(is_ordinal(casetype))
|
|
{$ifndef cpu64bit}
|
|
or is_64bitint(casetype)
|
|
{$endif cpu64bit}
|
|
then
|
|
Message(type_e_ordinal_expr_expected);
|
|
consume(_OF);
|
|
|
|
UnionSymtable:=trecordsymtable.create(current_settings.packrecords);
|
|
UnionDef:=trecorddef.create(unionsymtable);
|
|
uniondef.isunion:=true;
|
|
startvarrecsize:=UnionSymtable.datasize;
|
|
{ align the bitpacking to the next byte }
|
|
UnionSymtable.datasize:=startvarrecsize;
|
|
startvarrecalign:=UnionSymtable.fieldalignment;
|
|
startpadalign:=Unionsymtable.padalignment;
|
|
symtablestack.push(UnionSymtable);
|
|
repeat
|
|
repeat
|
|
pt:=comp_expr(true);
|
|
if not(pt.nodetype=ordconstn) then
|
|
Message(parser_e_illegal_expression);
|
|
if try_to_consume(_POINTPOINT) then
|
|
pt:=crangenode.create(pt,comp_expr(true));
|
|
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_record_fields([vd_record]);
|
|
dec(variantrecordlevel);
|
|
consume(_RKLAMMER);
|
|
{ calculates maximal variant size }
|
|
maxsize:=max(maxsize,unionsymtable.datasize);
|
|
maxalignment:=max(maxalignment,unionsymtable.fieldalignment);
|
|
maxpadalign:=max(maxpadalign,unionsymtable.padalignment);
|
|
{ the items of the next variant are overlayed }
|
|
unionsymtable.datasize:=startvarrecsize;
|
|
unionsymtable.fieldalignment:=startvarrecalign;
|
|
unionsymtable.padalignment:=startpadalign;
|
|
if (token<>_END) and (token<>_RKLAMMER) then
|
|
consume(_SEMICOLON)
|
|
else
|
|
break;
|
|
until (token=_END) or (token=_RKLAMMER);
|
|
symtablestack.pop(UnionSymtable);
|
|
{ at last set the record size to that of the biggest variant }
|
|
unionsymtable.datasize:=maxsize;
|
|
unionsymtable.fieldalignment:=maxalignment;
|
|
unionsymtable.addalignmentpadding;
|
|
{$ifdef powerpc}
|
|
{ parent inherits the alignment padding if the variant is the first "field" of the parent record/variant }
|
|
if (target_info.system in [system_powerpc_darwin, system_powerpc_macos]) and
|
|
is_first_field and
|
|
(recst.usefieldalignment = -1) and
|
|
(maxpadalign > recst.padalignment) then
|
|
recst.padalignment:=maxpadalign;
|
|
{$endif powerpc}
|
|
{ Align the offset where the union symtable is added }
|
|
if (recst.usefieldalignment=-1) then
|
|
usedalign:=used_align(unionsymtable.recordalignment,current_settings.alignment.recordalignmin,current_settings.alignment.maxCrecordalign)
|
|
else
|
|
usedalign:=used_align(unionsymtable.recordalignment,current_settings.alignment.recordalignmin,current_settings.alignment.recordalignmax);
|
|
|
|
offset:=align(recst.datasize,usedalign);
|
|
recst.datasize:=offset+unionsymtable.datasize;
|
|
|
|
if unionsymtable.recordalignment>recst.fieldalignment then
|
|
recst.fieldalignment:=unionsymtable.recordalignment;
|
|
|
|
trecordsymtable(recst).insertunionst(Unionsymtable,offset);
|
|
uniondef.owner.deletedef(uniondef);
|
|
end;
|
|
block_type:=old_block_type;
|
|
current_object_option:=old_current_object_option;
|
|
{ free the list }
|
|
sc.free;
|
|
{$ifdef powerpc}
|
|
is_first_field := false;
|
|
{$endif powerpc}
|
|
end;
|
|
|
|
end.
|