fpc/compiler/parser.pas
svenbarth f7f357f18e * symconst.pas:
- remove thelpersearch again => adjustments to searchsym_in_class and calls to it
- rename sto_has_classhelper to sto_has_helper
* symbase.pas: make push and pop in tsymtablestack virtual
* symdef.pas:
- add a new class tdefawaresymtablestack which overrides push and pop of tsymtablestack and adjusts the new extendeddefs field of the current tmodule
- tobjectdef.create: sto_has_classhelper => sto_has_helper
* fmodule.pas:
- add new hash object list to tmodule (key: mangled type name) which holds object list instances that contain all helpers currently active for a given type (= key of the hash list)
- tmodule.create: the hash list owns the sublists (but those don't own the defs)
- tmodule.destroy: free the hash list
* pdecobjpas:
- rename parse_extended_class to parse_extended_type
- parsing of constructors:
# for all helper types: no class constructors allowed
# for record helpers: as long as constructors for records themselves are disabled they are for record helpers as well
- object_dec: manually add the helper to the extendeddefs if the overall owner of the current def is a static symtable (implementation section or program/library main file), because the symtable won't be popped and pushed then
* parser.pas: instantiate the new stack class
* psub.pas: backup the extendeddefs in specialize_objectdefs as well
* ptype.pas:
- generate_specialization: backup the extendeddefs as well
- record_dec: _RECORD is now consumed in read_named_type itself
- read_named_type: parse "record helper" if advanced record syntax is enabled
* symtable.pas:
- correct searchsym_in_class declaration => adjustments in this unit as well
- add the possibility to pass a context def to search_last_objectpascal_helper
- rename search_objectpascal_class_helper to search_objectpascal_helper
- rename search_class_helper to search_objc_helper
- searchsym_in_class: 
# search for helpers in every level of the tree of the class
# the contextclassh can also be a subclass of the extendeddef
- searchsym_in_record: search for helper methods as well
- searchsym_in_helper:
# search for symbols in class and record helpers is the same except for the search in the class' ancestors
# search the extendeddef directly and rely on searchsym_in_class only for the class' ancestors as we need to find the helpers there as well
- search_last_objectpascal_helper: use the extendeddefs list of current_module instead of scanning the symbol stack itself
* pexpr.pas: adjustments because of renaming of sto_has_classhelper
* pinline.pas: adjustment because of removing of thelpersearch
* nflw.pas: 
- renamed classhelper to helperdef
- adjusted search_last_objectpascal_helper call
* msg*:
- adjusted error message for constructors in records (this currently applies to record helpers as well)
- renamed parser_e_not_allowed_in_classhelper to parser_e_not_allowed_in_helper => adjustments in code
- added parser_e_no_class_constructors_in_helpers
* pdecsub.pas: adjusted renamed error message
* htypechk.pas: check for helpers in every step of the hierarchy
* nobj.pas: search_class_helper => search_objc_helper
* utils/ppudump.pas: adjust, because of renames

Note: the define "useoldsearch" will be only used for performance comparison on my (faster) Linux machine; that (and its associated code) will be removed afterwards

git-svn-id: branches/svenbarth/classhelpers@17151 -
2011-03-20 11:27:27 +00:00

530 lines
17 KiB
ObjectPascal

{
Copyright (c) 1998-2002 by Florian Klaempfl
This unit does the parsing process
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 parser;
{$i fpcdefs.inc}
interface
{$ifdef PREPROCWRITE}
procedure preprocess(const filename:string);
{$endif PREPROCWRITE}
procedure compile(const filename:string);
procedure initparser;
procedure doneparser;
implementation
uses
{$IFNDEF USE_FAKE_SYSUTILS}
sysutils,
{$ELSE}
fksysutl,
{$ENDIF}
cutils,cclasses,
globtype,version,tokens,systems,globals,verbose,switches,
symbase,symtable,symdef,symsym,
finput,fmodule,fppu,
aasmbase,aasmtai,aasmdata,
cgbase,
script,gendef,
comphook,
scanner,scandir,
pbase,ptype,psystem,pmodules,psub,ncgrtti,htypechk,
cresstr,cpuinfo,procinfo;
procedure initparser;
begin
{ Current compiled module/proc }
set_current_module(nil);
current_module:=nil;
current_asmdata:=nil;
current_procinfo:=nil;
current_structdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
loaded_units:=TLinkedList.Create;
usedunits:=TLinkedList.Create;
unloaded_units:=TLinkedList.Create;
{ global switches }
current_settings.globalswitches:=init_settings.globalswitches;
current_settings.sourcecodepage:=init_settings.sourcecodepage;
{ initialize scanner }
InitScanner;
InitScannerDirectives;
{ scanner }
c:=#0;
pattern:='';
orgpattern:='';
cstringpattern:='';
current_scanner:=nil;
switchesstatestackpos:=0;
{ register all nodes and tais }
registernodes;
registertais;
{ memory sizes }
if stacksize=0 then
stacksize:=target_info.stacksize;
{ RTTI writer }
RTTIWriter:=TRTTIWriter.Create;
{ open assembler response }
if cs_link_on_target in current_settings.globalswitches then
GenerateAsmRes(outputexedir+ChangeFileExt(inputfilename,'_ppas'))
else
GenerateAsmRes(outputexedir+'ppas');
{ open deffile }
DefFile:=TDefFile.Create(outputexedir+ChangeFileExt(inputfilename,target_info.defext));
{ list of generated .o files, so the linker can remove them }
SmartLinkOFiles:=TCmdStrList.Create;
{ codegen }
if paraprintnodetree<>0 then
printnode_reset;
{ target specific stuff }
case target_info.system of
system_powerpc_amiga:
include(supported_calling_conventions,pocall_syscall);
system_powerpc_morphos:
include(supported_calling_conventions,pocall_syscall);
system_m68k_amiga:
include(supported_calling_conventions,pocall_syscall);
end;
end;
procedure doneparser;
begin
{ Reset current compiling info, so destroy routines can't
reference the data that might already be destroyed }
set_current_module(nil);
current_module:=nil;
current_procinfo:=nil;
current_asmdata:=nil;
current_structdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
{ unload units }
if assigned(loaded_units) then
begin
loaded_units.free;
loaded_units:=nil;
end;
if assigned(usedunits) then
begin
usedunits.free;
usedunits:=nil;
end;
if assigned(unloaded_units) then
begin
unloaded_units.free;
unloaded_units:=nil;
end;
{ if there was an error in the scanner, the scanner is
still assinged }
if assigned(current_scanner) then
begin
current_scanner.free;
current_scanner:=nil;
end;
{ close scanner }
DoneScanner;
RTTIWriter.free;
{ close ppas,deffile }
asmres.free;
deffile.free;
{ free list of .o files }
SmartLinkOFiles.Free;
end;
{$ifdef PREPROCWRITE}
procedure preprocess(const filename:string);
var
i : longint;
begin
new(preprocfile,init('pre'));
{ initialize a module }
set_current_module(new(pmodule,init(filename,false)));
macrosymtablestack:= initialmacrosymtable;
current_module.localmacrosymtable:= tmacrosymtable.create(false);
current_module.localmacrosymtable.next:= initialmacrosymtable;
macrosymtablestack:= current_module.localmacrosymtable;
main_module:=current_module;
{ startup scanner, and save in current_module }
current_scanner:=new(pscannerfile,Init(filename));
current_module.scanner:=current_scanner;
{ loop until EOF is found }
repeat
current_scanner^.readtoken(true);
preprocfile^.AddSpace;
case token of
_ID :
begin
preprocfile^.Add(orgpattern);
end;
_REALNUMBER,
_INTCONST :
preprocfile^.Add(pattern);
_CSTRING :
begin
i:=0;
while (i<length(cstringpattern)) do
begin
inc(i);
if cstringpattern[i]='''' then
begin
insert('''',cstringpattern,i);
inc(i);
end;
end;
preprocfile^.Add(''''+cstringpattern+'''');
end;
_CCHAR :
begin
case pattern[1] of
#39 :
pattern:='''''''';
#0..#31,
#128..#255 :
begin
str(ord(pattern[1]),pattern);
pattern:='#'+pattern;
end;
else
pattern:=''''+pattern[1]+'''';
end;
preprocfile^.Add(pattern);
end;
_EOF :
break;
else
preprocfile^.Add(tokeninfo^[token].str)
end;
until false;
{ free scanner }
dispose(current_scanner,done);
current_scanner:=nil;
{ close }
dispose(preprocfile,done);
end;
{$endif PREPROCWRITE}
{*****************************************************************************
Compile a source file
*****************************************************************************}
procedure compile(const filename:string);
type
polddata=^tolddata;
tolddata=record
{ scanner }
oldidtoken,
oldtoken : ttoken;
oldtokenpos : tfileposinfo;
oldc : char;
oldpattern,
oldorgpattern : string;
old_block_type : tblock_type;
{ symtable }
oldsymtablestack,
oldmacrosymtablestack : TSymtablestack;
oldaktprocsym : tprocsym;
{ cg }
oldparse_only : boolean;
{ akt.. things }
oldcurrent_filepos : tfileposinfo;
old_current_module : tmodule;
oldcurrent_procinfo : tprocinfo;
old_settings : tsettings;
oldsourcecodepage : tcodepagestring;
old_switchesstatestack : tswitchesstatestack;
old_switchesstatestackpos : Integer;
end;
var
olddata : polddata;
hp,hp2 : tmodule;
begin
{ parsing a procedure or declaration should be finished }
if assigned(current_procinfo) then
internalerror(200811121);
if assigned(current_structdef) then
internalerror(200811122);
inc(compile_level);
parser_current_file:=filename;
{ Uses heap memory instead of placing everything on the
stack. This is needed because compile() can be called
recursively }
new(olddata);
with olddata^ do
begin
old_current_module:=current_module;
{ save symtable state }
oldsymtablestack:=symtablestack;
oldmacrosymtablestack:=macrosymtablestack;
oldcurrent_procinfo:=current_procinfo;
{ save scanner state }
oldc:=c;
oldpattern:=pattern;
oldorgpattern:=orgpattern;
oldtoken:=token;
oldidtoken:=idtoken;
old_block_type:=block_type;
oldtokenpos:=current_tokenpos;
old_switchesstatestack:=switchesstatestack;
old_switchesstatestackpos:=switchesstatestackpos;
{ save cg }
oldparse_only:=parse_only;
{ save akt... state }
{ handle the postponed case first }
flushpendingswitchesstate;
oldcurrent_filepos:=current_filepos;
old_settings:=current_settings;
end;
{ reset parser, a previous fatal error could have left these variables in an unreliable state, this is
important for the IDE }
afterassignment:=false;
in_args:=false;
named_args_allowed:=false;
got_addrn:=false;
getprocvardef:=nil;
allow_array_constructor:=false;
{ show info }
Message1(parser_i_compiling,filename);
{ reset symtable }
{$ifdef useoldsearch}
symtablestack:=tsymtablestack.create;
{$else}
symtablestack:=tdefawaresymtablestack.create;
{$endif}
macrosymtablestack:=TSymtablestack.create;
systemunit:=nil;
current_settings.defproccall:=init_settings.defproccall;
current_exceptblock:=0;
exceptblockcounter:=0;
current_settings.maxfpuregisters:=-1;
{ reset the unit or create a new program }
{ a unit compiled at command line must be inside the loaded_unit list }
if (compile_level=1) then
begin
if assigned(current_module) then
internalerror(200501158);
set_current_module(tppumodule.create(nil,filename,'',false));
addloadedunit(current_module);
main_module:=current_module;
current_module.state:=ms_compile;
end;
if not(assigned(current_module) and
(current_module.state in [ms_compile,ms_second_compile])) then
internalerror(200212281);
{ Load current state from the init values }
current_settings:=init_settings;
{ load current asmdata from current_module }
current_asmdata:=TAsmData(current_module.asmdata);
{ startup scanner and load the first file }
current_scanner:=tscannerfile.Create(filename);
current_scanner.firstfile;
current_module.scanner:=current_scanner;
{ init macros before anything in the file is parsed.}
current_module.localmacrosymtable:= tmacrosymtable.create(false);
macrosymtablestack.push(initialmacrosymtable);
macrosymtablestack.push(current_module.localmacrosymtable);
{ read the first token }
current_scanner.readtoken(false);
{ If the compile level > 1 we get a nice "unit expected" error
message if we are trying to use a program as unit.}
try
try
if (token=_UNIT) or (compile_level>1) then
begin
current_module.is_unit:=true;
proc_unit;
end
else if (token=_ID) and (idtoken=_PACKAGE) then
begin
current_module.IsPackage:=true;
proc_package;
end
else
proc_program(token=_LIBRARY);
except
on ECompilerAbort do
raise;
on Exception do
begin
{ Increase errorcounter to prevent some
checks during cleanup }
inc(status.errorcount);
raise;
end;
end;
finally
if assigned(current_module) then
begin
{ module is now compiled }
tppumodule(current_module).state:=ms_compiled;
{ free ppu }
if assigned(tppumodule(current_module).ppufile) then
begin
tppumodule(current_module).ppufile.free;
tppumodule(current_module).ppufile:=nil;
end;
{ free asmdata }
if assigned(current_module.asmdata) then
begin
current_module.asmdata.free;
current_module.asmdata:=nil;
end;
{ free scanner }
if assigned(current_module.scanner) then
begin
if current_scanner=tscannerfile(current_module.scanner) then
current_scanner:=nil;
tscannerfile(current_module.scanner).free;
current_module.scanner:=nil;
end;
{ free symtable stack }
if assigned(symtablestack) then
begin
symtablestack.free;
symtablestack:=nil;
end;
if assigned(macrosymtablestack) then
begin
macrosymtablestack.free;
macrosymtablestack:=nil;
end;
end;
if (compile_level=1) and
(status.errorcount=0) then
{ Write Browser Collections }
do_extractsymbolinfo;
with olddata^ do
begin
{ restore scanner }
c:=oldc;
pattern:=oldpattern;
orgpattern:=oldorgpattern;
token:=oldtoken;
idtoken:=oldidtoken;
current_tokenpos:=oldtokenpos;
block_type:=old_block_type;
switchesstatestack:=old_switchesstatestack;
switchesstatestackpos:=old_switchesstatestackpos;
{ restore cg }
parse_only:=oldparse_only;
{ restore symtable state }
symtablestack:=oldsymtablestack;
macrosymtablestack:=oldmacrosymtablestack;
current_procinfo:=oldcurrent_procinfo;
current_filepos:=oldcurrent_filepos;
current_settings:=old_settings;
current_exceptblock:=0;
exceptblockcounter:=0;
end;
{ Shut down things when the last file is compiled succesfull }
if (compile_level=1) and
(status.errorcount=0) then
begin
parser_current_file:='';
{ Close script }
if (not AsmRes.Empty) then
begin
Message1(exec_i_closing_script,AsmRes.Fn);
AsmRes.WriteToDisk;
end;
end;
{ free now what we did not free earlier in
proc_program PM }
if (compile_level=1) and needsymbolinfo then
begin
hp:=tmodule(loaded_units.first);
while assigned(hp) do
begin
hp2:=tmodule(hp.next);
if (hp<>current_module) then
begin
loaded_units.remove(hp);
hp.free;
end;
hp:=hp2;
end;
{ free also unneeded units we didn't free before }
unloaded_units.Clear;
end;
dec(compile_level);
set_current_module(olddata^.old_current_module);
dispose(olddata);
end;
end;
end.