fpc/compiler/scanner.pas
Jonas Maebe eccbc78e04 + support for bitpacked arrays:
+ use {$bitpacking on/+} to change the meaning of "packed"
      into "bitpacked" for arrays. This is the default for MacPas.
      You can also define individual arrays as "bitpacked", but
      this is not encouraged since this keyword is not known by
      other compilers and therefore makes your code unportable.
    + pack(unpackedarray,index,packedarray) to pack
      length(packedarray) elements starting at
      unpackedarray[index] into packedarray.
    + unpack(packedarray,unpackedarray,index) to unpack
      packedarray into unpackedarray, with the first
      element being stored at unpackedarray[index]
  * todo:
    * "open packed arrays" and rtti for packed arrays are not
      yet supported
    * gdb does not properly support bitpacked arrays

git-svn-id: trunk@4449 -
2006-08-19 12:54:12 +00:00

3957 lines
124 KiB
ObjectPascal

{
Copyright (c) 1998-2002 by Florian Klaempfl
This unit implements the scanner part and handling of the switches
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 scanner;
{$i fpcdefs.inc}
interface
uses
cclasses,
globtype,globals,version,tokens,
verbose,comphook,
finput,
widestr;
const
max_include_nesting=32;
max_macro_nesting=16;
preprocbufsize=32*1024;
type
tcommentstyle = (comment_none,comment_tp,comment_oldtp,comment_delphi,comment_c);
tscannerfile = class;
preproctyp = (pp_ifdef,pp_ifndef,pp_if,pp_ifopt,pp_else,pp_elseif);
tpreprocstack = class
typ : preproctyp;
accept : boolean;
next : tpreprocstack;
name : stringid;
line_nb : longint;
owner : tscannerfile;
constructor Create(atyp:preproctyp;a:boolean;n:tpreprocstack);
end;
tdirectiveproc=procedure;
tdirectiveitem = class(TNamedIndexItem)
public
is_conditional : boolean;
proc : tdirectiveproc;
constructor Create(const n:string;p:tdirectiveproc);
constructor CreateCond(const n:string;p:tdirectiveproc);
end;
tcompile_time_predicate = function(var valuedescr: String) : Boolean;
tscannerfile = class
public
inputfile : tinputfile; { current inputfile list }
inputfilecount : longint;
inputbuffer, { input buffer }
inputpointer : pchar;
inputstart : longint;
line_no, { line }
lastlinepos : longint;
lasttokenpos : longint; { token }
lasttoken,
nexttoken : ttoken;
replaysavetoken : ttoken;
replaytokenbuf,
recordtokenbuf : tdynamicarray;
comment_level,
yylexcount : longint;
lastasmgetchar : char;
ignoredirectives : tstringlist; { ignore directives, used to give warnings only once }
preprocstack : tpreprocstack;
in_asm_string : boolean;
preproc_pattern : string;
preproc_token : ttoken;
constructor Create(const fn:string);
destructor Destroy;override;
{ File buffer things }
function openinputfile:boolean;
procedure closeinputfile;
function tempopeninputfile:boolean;
procedure tempcloseinputfile;
procedure saveinputfile;
procedure restoreinputfile;
procedure firstfile;
procedure nextfile;
procedure addfile(hp:tinputfile);
procedure reload;
procedure insertmacro(const macname:string;p:pchar;len,line,fileindex:longint);
{ Scanner things }
procedure gettokenpos;
procedure inc_comment_level;
procedure dec_comment_level;
procedure illegal_char(c:char);
procedure end_of_file;
procedure checkpreprocstack;
procedure poppreprocstack;
procedure ifpreprocstack(atyp : preproctyp;compile_time_predicate:tcompile_time_predicate;messid:longint);
procedure elseifpreprocstack(compile_time_predicate:tcompile_time_predicate);
procedure elsepreprocstack;
procedure handleconditional(p:tdirectiveitem);
procedure handledirectives;
procedure linebreak;
procedure recordtoken;
procedure startrecordtokens(buf:tdynamicarray);
procedure stoprecordtokens;
procedure replaytoken;
procedure startreplaytokens(buf:tdynamicarray);
procedure readchar;
procedure readstring;
procedure readnumber;
function readid:string;
function readval:longint;
function readval_asstring:string;
function readcomment:string;
function readquotedstring:string;
function readstate:char;
function readstatedefault:char;
procedure skipspace;
procedure skipuntildirective;
procedure skipcomment;
procedure skipdelphicomment;
procedure skipoldtpcomment;
procedure readtoken(allowrecordtoken:boolean);
function readpreproc:ttoken;
function asmgetcharstart : char;
function asmgetchar:char;
end;
{$ifdef PREPROCWRITE}
tpreprocfile=class
f : text;
buf : pointer;
spacefound,
eolfound : boolean;
constructor create(const fn:string);
destructor destroy;
procedure Add(const s:string);
procedure AddSpace;
end;
{$endif PREPROCWRITE}
var
{ read strings }
c : char;
orgpattern,
pattern : string;
patternw : pcompilerwidestring;
{ token }
token, { current token being parsed }
idtoken : ttoken; { holds the token if the pattern is a known word }
current_scanner : tscannerfile; { current scanner in use }
aktcommentstyle : tcommentstyle; { needed to use read_comment from directives }
{$ifdef PREPROCWRITE}
preprocfile : tpreprocfile; { used with only preprocessing }
{$endif PREPROCWRITE}
type
tdirectivemode = (directive_all, directive_turbo, directive_mac);
procedure AddDirective(const s:string; dm: tdirectivemode; p:tdirectiveproc);
procedure AddConditional(const s:string; dm: tdirectivemode; p:tdirectiveproc);
procedure InitScanner;
procedure DoneScanner;
{To be called when the language mode is finally determined}
Function SetCompileMode(const s:string; changeInit: boolean):boolean;
implementation
uses
dos,
cutils,
systems,
switches,
symbase,symtable,symtype,symsym,symconst,symdef,defutil,
fmodule;
var
{ dictionaries with the supported directives }
turbo_scannerdirectives : tdictionary; { for other modes }
mac_scannerdirectives : tdictionary; { for mode mac }
{*****************************************************************************
Helper routines
*****************************************************************************}
const
{ use any special name that is an invalid file name to avoid problems }
preprocstring : array [preproctyp] of string[7]
= ('$IFDEF','$IFNDEF','$IF','$IFOPT','$ELSE','$ELSEIF');
function is_keyword(const s:string):boolean;
var
low,high,mid : longint;
begin
if not (length(s) in [tokenlenmin..tokenlenmax]) or
not (s[1] in ['a'..'z','A'..'Z']) then
begin
is_keyword:=false;
exit;
end;
low:=ord(tokenidx^[length(s),s[1]].first);
high:=ord(tokenidx^[length(s),s[1]].last);
while low<high do
begin
mid:=(high+low+1) shr 1;
if pattern<tokeninfo^[ttoken(mid)].str then
high:=mid-1
else
low:=mid;
end;
is_keyword:=(pattern=tokeninfo^[ttoken(high)].str) and
(tokeninfo^[ttoken(high)].keyword in aktmodeswitches);
end;
Function SetCompileMode(const s:string; changeInit: boolean):boolean;
var
b : boolean;
oldaktmodeswitches : tmodeswitches;
begin
oldaktmodeswitches:=aktmodeswitches;
b:=true;
if s='DEFAULT' then
aktmodeswitches:=initmodeswitches
else
if s='DELPHI' then
aktmodeswitches:=delphimodeswitches
else
if s='TP' then
aktmodeswitches:=tpmodeswitches
else
if s='FPC' then
aktmodeswitches:=fpcmodeswitches
else
if s='OBJFPC' then
aktmodeswitches:=objfpcmodeswitches
else
if s='GPC' then
aktmodeswitches:=gpcmodeswitches
else
if s='MACPAS' then
aktmodeswitches:=macmodeswitches
else
b:=false;
if b and changeInit then
initmodeswitches := aktmodeswitches;
if b then
begin
{ resolve all postponed switch changes }
if localswitcheschanged then
begin
aktlocalswitches:=nextaktlocalswitches;
localswitcheschanged:=false;
end;
{ turn ansistrings on by default ? }
if (m_delphi in aktmodeswitches) then
begin
include(aktlocalswitches,cs_ansistrings);
if changeinit then
include(initlocalswitches,cs_ansistrings);
end
else
begin
exclude(aktlocalswitches,cs_ansistrings);
if changeinit then
exclude(initlocalswitches,cs_ansistrings);
end;
{ turn on bitpacking for mode macpas }
if (m_mac in aktmodeswitches) then
begin
include(aktlocalswitches,cs_bitpacking);
if changeinit then
include(initlocalswitches,cs_bitpacking);
end;
{ support goto/label by default in delphi/tp7/mac modes }
if ([m_delphi,m_tp7,m_mac] * aktmodeswitches <> []) then
begin
include(aktmoduleswitches,cs_support_goto);
if changeinit then
include(initmoduleswitches,cs_support_goto);
end;
{ Default enum packing for delphi/tp7 }
if (m_tp7 in aktmodeswitches) or
(m_delphi in aktmodeswitches) then
aktpackenum:=1
else if (m_mac in aktmodeswitches) then
{ compatible with Metrowerks Pascal }
aktpackenum:=2
else
aktpackenum:=4;
if changeinit then
initpackenum:=aktpackenum;
{$ifdef i386}
{ Default to intel assembler for delphi/tp7 on i386 }
if (m_delphi in aktmodeswitches) or
(m_tp7 in aktmodeswitches) then
aktasmmode:=asmmode_i386_intel;
if changeinit then
initasmmode:=aktasmmode;
{$endif i386}
{ Undefine old symbol }
if (m_delphi in oldaktmodeswitches) then
undef_system_macro('FPC_DELPHI')
else if (m_tp7 in oldaktmodeswitches) then
undef_system_macro('FPC_TP')
else if (m_objfpc in oldaktmodeswitches) then
undef_system_macro('FPC_OBJFPC')
else if (m_gpc in oldaktmodeswitches) then
undef_system_macro('FPC_GPC')
else if (m_mac in oldaktmodeswitches) then
undef_system_macro('FPC_MACPAS');
{ define new symbol in delphi,objfpc,tp,gpc,macpas mode }
if (m_delphi in aktmodeswitches) then
def_system_macro('FPC_DELPHI')
else if (m_tp7 in aktmodeswitches) then
def_system_macro('FPC_TP')
else if (m_objfpc in aktmodeswitches) then
def_system_macro('FPC_OBJFPC')
else if (m_gpc in aktmodeswitches) then
def_system_macro('FPC_GPC')
else if (m_mac in aktmodeswitches) then
def_system_macro('FPC_MACPAS');
end;
SetCompileMode:=b;
end;
{*****************************************************************************
Conditional Directives
*****************************************************************************}
procedure dir_else;
begin
current_scanner.elsepreprocstack;
end;
procedure dir_endif;
begin
current_scanner.poppreprocstack;
end;
function isdef(var valuedescr: String): Boolean;
var
hs : string;
mac : tmacro;
begin
current_scanner.skipspace;
hs:=current_scanner.readid;
valuedescr:= hs;
if hs='' then
Message(scan_e_error_in_preproc_expr);
mac:=tmacro(search_macro(hs));
if assigned(mac) then
mac.is_used:=true;
isdef:= assigned(mac) and mac.defined;
end;
procedure dir_ifdef;
begin
current_scanner.ifpreprocstack(pp_ifdef,@isdef,scan_c_ifdef_found);
end;
function isnotdef(var valuedescr: String): Boolean;
var
hs : string;
mac : tmacro;
begin
current_scanner.skipspace;
hs:=current_scanner.readid;
valuedescr:= hs;
if hs='' then
Message(scan_e_error_in_preproc_expr);
mac:=tmacro(search_macro(hs));
if assigned(mac) then
mac.is_used:=true;
isnotdef:= not (assigned(mac) and mac.defined);
end;
procedure dir_ifndef;
begin
current_scanner.ifpreprocstack(pp_ifndef,@isnotdef,scan_c_ifndef_found);
end;
function opt_check(var valuedescr: String): Boolean;
var
hs : string;
state : char;
begin
opt_check:= false;
current_scanner.skipspace;
hs:=current_scanner.readid;
valuedescr:= hs;
if (length(hs)>1) then
Message1(scan_w_illegal_switch,hs)
else
begin
state:=current_scanner.ReadState;
if state in ['-','+'] then
opt_check:=CheckSwitch(hs[1],state)
else
Message(scan_e_error_in_preproc_expr);
end;
end;
procedure dir_ifopt;
begin
current_scanner.ifpreprocstack(pp_ifopt,@opt_check,scan_c_ifopt_found);
end;
procedure dir_libprefix;
var
s : string;
begin
current_scanner.skipspace;
if c <> '''' then
Message2(scan_f_syn_expected, '''', c);
s := current_scanner.readquotedstring;
stringdispose(outputprefix);
outputprefix := stringdup(s);
with current_module do
setfilename(paramfn^, paramallowoutput);
end;
procedure dir_libsuffix;
var
s : string;
begin
current_scanner.skipspace;
if c <> '''' then
Message2(scan_f_syn_expected, '''', c);
s := current_scanner.readquotedstring;
stringdispose(outputsuffix);
outputsuffix := stringdup(s);
with current_module do
setfilename(paramfn^, paramallowoutput);
end;
procedure dir_extension;
var
s : string;
begin
current_scanner.skipspace;
if c <> '''' then
Message2(scan_f_syn_expected, '''', c);
s := current_scanner.readquotedstring;
outputextension := '.'+s;
with current_module do
setfilename(paramfn^, paramallowoutput);
end;
{
Compile time expression type check
----------------------------------
Each subexpression returns its type to the caller, which then can
do type check. Since data types of compile time expressions is
not well defined, the type system does a best effort. The drawback is
that some errors might not be detected.
Instead of returning a particular data type, a set of possible data types
are returned. This way ambigouos types can be handled. For instance a
value of 1 can be both a boolean and and integer.
Booleans
--------
The following forms of boolean values are supported:
* C coded, that is 0 is false, non-zero is true.
* TRUE/FALSE for mac style compile time variables
Thus boolean mac compile time variables are always stored as TRUE/FALSE.
When a compile time expression is evaluated, they are then translated
to C coded booleans (0/1), to simplify for the expression evaluator.
Note that this scheme then also of support mac compile time variables which
are 0/1 but with a boolean meaning.
The TRUE/FALSE format is new from 22 august 2005, but the above scheme
means that units which is not recompiled, and thus stores
compile time variables as the old format (0/1), continue to work.
Short circuit evaluation
------------------------
For this to work, the part of a compile time expression which is short
circuited, should not be evaluated, while it still should be parsed.
Therefor there is a parameter eval, telling whether evaluation is needed.
In case not, the value returned can be arbitrary.
}
type
{Compile time expression types}
TCTEType = (ctetBoolean, ctetInteger, ctetString, ctetSet);
TCTETypeSet = set of TCTEType;
const
cteTypeNames : array[TCTEType] of string[10] = (
'BOOLEAN','INTEGER','STRING','SET');
{Subset of types which can be elements in sets.}
setElementTypes = [ctetBoolean, ctetInteger, ctetString];
function GetCTETypeName(t: TCTETypeSet): String;
var
i: TCTEType;
begin
result:= '';
for i:= Low(TCTEType) to High(TCTEType) do
if i in t then
if result = '' then
result:= cteTypeNames[i]
else
result:= result + ' or ' + cteTypeNames[i];
end;
procedure CTEError(actType, desiredExprType: TCTETypeSet; place: String);
begin
Message3(scan_e_compile_time_typeerror,
GetCTETypeName(desiredExprType),
GetCTETypeName(actType),
place
);
end;
function parse_compiler_expr(var compileExprType: TCTETypeSet):string;
function read_expr(var exprType: TCTETypeSet; eval : Boolean) : string; forward;
procedure preproc_consume(t : ttoken);
begin
if t<>current_scanner.preproc_token then
Message(scan_e_preproc_syntax_error);
current_scanner.preproc_token:=current_scanner.readpreproc;
end;
function preproc_substitutedtoken(var macroType: TCTETypeSet; eval : Boolean): string;
{ Currently this parses identifiers as well as numbers.
The result from this procedure can either be that the token
itself is a value, or that it is a compile time variable/macro,
which then is substituted for another value (for macros
recursivelly substituted).}
var
hs: string;
mac : tmacro;
macrocount,
len : integer;
numres : longint;
w: word;
begin
result := current_scanner.preproc_pattern;
if not eval then
exit;
mac:= nil;
{ Substitue macros and compiler variables with their content/value.
For real macros also do recursive substitution. }
macrocount:=0;
repeat
mac:=tmacro(search_macro(result));
inc(macrocount);
if macrocount>max_macro_nesting then
begin
Message(scan_w_macro_too_deep);
break;
end;
if assigned(mac) and mac.defined then
if assigned(mac.buftext) then
begin
if mac.buflen>255 then
begin
len:=255;
Message(scan_w_macro_cut_after_255_chars);
end
else
len:=mac.buflen;
hs[0]:=char(len);
move(mac.buftext^,hs[1],len);
result:=upcase(hs);
mac.is_used:=true;
end
else
begin
Message1(scan_e_error_macro_lacks_value, result);
break;
end
else
begin
break;
end;
if mac.is_compiler_var then
break;
until false;
{ At this point, result do contain the value. Do some decoding and
determine the type.}
val(result,numres,w);
if (w=0) then {It is an integer}
begin
if (numres = 0) or (numres = 1) then
macroType := [ctetInteger, ctetBoolean]
else
macroType := [ctetInteger];
end
else if assigned(mac) and (m_mac in aktmodeswitches) and (result='FALSE') then
begin
result:= '0';
macroType:= [ctetBoolean];
end
else if assigned(mac) and (m_mac in aktmodeswitches) and (result='TRUE') then
begin
result:= '1';
macroType:= [ctetBoolean];
end
else if (m_mac in aktmodeswitches) and
(not assigned(mac) or not mac.defined) and
(macrocount = 1) then
begin
{Errors in mode mac is issued here. For non macpas modes there is
more liberty, but the error will eventually be caught at a later stage.}
Message1(scan_e_error_macro_undefined, result);
macroType:= [ctetString]; {Just to have something}
end
else
macroType:= [ctetString];
end;
function read_factor(var factorType: TCTETypeSet; eval : Boolean) : string;
var
hs : string;
mac: tmacro;
srsym : tsym;
srsymtable : tsymtable;
l : longint;
w : integer;
hasKlammer: Boolean;
setElemType : TCTETypeSet;
begin
if current_scanner.preproc_token=_ID then
begin
if current_scanner.preproc_pattern='DEFINED' then
begin
factorType:= [ctetBoolean];
preproc_consume(_ID);
current_scanner.skipspace;
if current_scanner.preproc_token =_LKLAMMER then
begin
preproc_consume(_LKLAMMER);
current_scanner.skipspace;
hasKlammer:= true;
end
else if (m_mac in aktmodeswitches) then
hasKlammer:= false
else
Message(scan_e_error_in_preproc_expr);
if current_scanner.preproc_token =_ID then
begin
hs := current_scanner.preproc_pattern;
mac := tmacro(search_macro(hs));
if assigned(mac) and mac.defined then
begin
hs := '1';
mac.is_used:=true;
end
else
hs := '0';
read_factor := hs;
preproc_consume(_ID);
current_scanner.skipspace;
end
else
Message(scan_e_error_in_preproc_expr);
if hasKlammer then
if current_scanner.preproc_token =_RKLAMMER then
preproc_consume(_RKLAMMER)
else
Message(scan_e_error_in_preproc_expr);
end
else
if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='UNDEFINED') then
begin
factorType:= [ctetBoolean];
preproc_consume(_ID);
current_scanner.skipspace;
if current_scanner.preproc_token =_ID then
begin
hs := current_scanner.preproc_pattern;
mac := tmacro(search_macro(hs));
if assigned(mac) then
begin
hs := '0';
mac.is_used:=true;
end
else
hs := '1';
read_factor := hs;
preproc_consume(_ID);
current_scanner.skipspace;
end
else
Message(scan_e_error_in_preproc_expr);
end
else
if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='OPTION') then
begin
factorType:= [ctetBoolean];
preproc_consume(_ID);
current_scanner.skipspace;
if current_scanner.preproc_token =_LKLAMMER then
begin
preproc_consume(_LKLAMMER);
current_scanner.skipspace;
end
else
Message(scan_e_error_in_preproc_expr);
if not (current_scanner.preproc_token = _ID) then
Message(scan_e_error_in_preproc_expr);
hs:=current_scanner.preproc_pattern;
if (length(hs) > 1) then
{This is allowed in Metrowerks Pascal}
Message(scan_e_error_in_preproc_expr)
else
begin
if CheckSwitch(hs[1],'+') then
read_factor := '1'
else
read_factor := '0';
end;
preproc_consume(_ID);
current_scanner.skipspace;
if current_scanner.preproc_token =_RKLAMMER then
preproc_consume(_RKLAMMER)
else
Message(scan_e_error_in_preproc_expr);
end
else
if current_scanner.preproc_pattern='SIZEOF' then
begin
factorType:= [ctetInteger];
preproc_consume(_ID);
current_scanner.skipspace;
if current_scanner.preproc_token =_LKLAMMER then
begin
preproc_consume(_LKLAMMER);
current_scanner.skipspace;
end
else
Message(scan_e_preproc_syntax_error);
if eval then
if searchsym(current_scanner.preproc_pattern,srsym,srsymtable) then
begin
l:=0;
case srsym.typ of
globalvarsym,
localvarsym,
paravarsym :
l:=tabstractvarsym(srsym).getsize;
typedconstsym :
l:=ttypedconstsym(srsym).getsize;
typesym:
l:=ttypesym(srsym).restype.def.size;
else
Message(scan_e_error_in_preproc_expr);
end;
str(l,read_factor);
end
else
Message1(sym_e_id_not_found,current_scanner.preproc_pattern);
preproc_consume(_ID);
current_scanner.skipspace;
if current_scanner.preproc_token =_RKLAMMER then
preproc_consume(_RKLAMMER)
else
Message(scan_e_preproc_syntax_error);
end
else
if current_scanner.preproc_pattern='DECLARED' then
begin
factorType:= [ctetBoolean];
preproc_consume(_ID);
current_scanner.skipspace;
if current_scanner.preproc_token =_LKLAMMER then
begin
preproc_consume(_LKLAMMER);
current_scanner.skipspace;
end
else
Message(scan_e_error_in_preproc_expr);
if current_scanner.preproc_token =_ID then
begin
hs := upper(current_scanner.preproc_pattern);
if searchsym(hs,srsym,srsymtable) then
hs := '1'
else
hs := '0';
read_factor := hs;
preproc_consume(_ID);
current_scanner.skipspace;
end
else
Message(scan_e_error_in_preproc_expr);
if current_scanner.preproc_token =_RKLAMMER then
preproc_consume(_RKLAMMER)
else
Message(scan_e_error_in_preproc_expr);
end
else
if current_scanner.preproc_pattern='NOT' then
begin
factorType:= [ctetBoolean];
preproc_consume(_ID);
hs:=read_factor(factorType, eval);
if eval then
begin
if not (ctetBoolean in factorType) then
CTEError(factorType, [ctetBoolean], 'NOT');
val(hs,l,w);
if l<>0 then
read_factor:='0'
else
read_factor:='1';
end
else
read_factor:='0'; {Just to have something}
end
else
if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='TRUE') then
begin
factorType:= [ctetBoolean];
preproc_consume(_ID);
read_factor:='1';
end
else
if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='FALSE') then
begin
factorType:= [ctetBoolean];
preproc_consume(_ID);
read_factor:='0';
end
else
begin
hs:=preproc_substitutedtoken(factorType, eval);
{ Default is to return the original symbol }
read_factor:=hs;
if eval and (m_delphi in aktmodeswitches) and (ctetString in factorType) then
if searchsym(current_scanner.preproc_pattern,srsym,srsymtable) then
begin
case srsym.typ of
constsym :
begin
with tconstsym(srsym) do
begin
case consttyp of
constord :
begin
case consttype.def.deftype of
orddef:
begin
if is_integer(consttype.def) then
begin
read_factor:=tostr(value.valueord);
factorType:= [ctetInteger];
end
else if is_boolean(consttype.def) then
begin
read_factor:=tostr(value.valueord);
factorType:= [ctetBoolean];
end
else if is_char(consttype.def) then
begin
read_factor:=chr(value.valueord);
factorType:= [ctetString];
end
end;
enumdef:
begin
read_factor:=tostr(value.valueord);
factorType:= [ctetInteger];
end;
end;
end;
conststring :
begin
read_factor := upper(pchar(value.valueptr));
factorType:= [ctetString];
end;
constset :
begin
hs:=',';
for l:=0 to 255 do
if l in pconstset(tconstsym(srsym).value.valueptr)^ then
hs:=hs+tostr(l)+',';
read_factor := hs;
factorType:= [ctetSet];
end;
end;
end;
end;
enumsym :
begin
read_factor:=tostr(tenumsym(srsym).value);
factorType:= [ctetInteger];
end;
end;
end;
preproc_consume(_ID);
current_scanner.skipspace;
end
end
else if current_scanner.preproc_token =_LKLAMMER then
begin
preproc_consume(_LKLAMMER);
read_factor:=read_expr(factorType, eval);
preproc_consume(_RKLAMMER);
end
else if current_scanner.preproc_token = _LECKKLAMMER then
begin
preproc_consume(_LECKKLAMMER);
read_factor := ',';
while current_scanner.preproc_token = _ID do
begin
read_factor := read_factor+read_factor(setElemType, eval)+',';
if current_scanner.preproc_token = _COMMA then
preproc_consume(_COMMA);
end;
// TODO Add check of setElemType
preproc_consume(_RECKKLAMMER);
factorType:= [ctetSet];
end
else
Message(scan_e_error_in_preproc_expr);
end;
function read_term(var termType: TCTETypeSet; eval : Boolean) : string;
var
hs1,hs2 : string;
l1,l2 : longint;
w : integer;
termType2: TCTETypeSet;
begin
hs1:=read_factor(termType, eval);
repeat
if (current_scanner.preproc_token<>_ID) then
break;
if current_scanner.preproc_pattern<>'AND' then
break;
val(hs1,l1,w);
if l1=0 then
eval:= false; {Short circuit evaluation of OR}
if eval then
begin
{Check if first expr is boolean. Must be done here, after we know
it is an AND expression.}
if not (ctetBoolean in termType) then
CTEError(termType, [ctetBoolean], 'AND');
termType:= [ctetBoolean];
end;
preproc_consume(_ID);
hs2:=read_factor(termType2, eval);
if eval then
begin
if not (ctetBoolean in termType2) then
CTEError(termType2, [ctetBoolean], 'AND');
val(hs2,l2,w);
if (l1<>0) and (l2<>0) then
hs1:='1'
else
hs1:='0';
end;
until false;
read_term:=hs1;
end;
function read_simple_expr(var simpleExprType: TCTETypeSet; eval : Boolean) : string;
var
hs1,hs2 : string;
l1,l2 : longint;
w : integer;
simpleExprType2: TCTETypeSet;
begin
hs1:=read_term(simpleExprType, eval);
repeat
if (current_scanner.preproc_token<>_ID) then
break;
if current_scanner.preproc_pattern<>'OR' then
break;
val(hs1,l1,w);
if l1<>0 then
eval:= false; {Short circuit evaluation of OR}
if eval then
begin
{Check if first expr is boolean. Must be done here, after we know
it is an OR expression.}
if not (ctetBoolean in simpleExprType) then
CTEError(simpleExprType, [ctetBoolean], 'OR');
simpleExprType:= [ctetBoolean];
end;
preproc_consume(_ID);
hs2:=read_term(simpleExprType2, eval);
if eval then
begin
if not (ctetBoolean in simpleExprType2) then
CTEError(simpleExprType2, [ctetBoolean], 'OR');
val(hs2,l2,w);
if (l1<>0) or (l2<>0) then
hs1:='1'
else
hs1:='0';
end;
until false;
read_simple_expr:=hs1;
end;
function read_expr(var exprType: TCTETypeSet; eval : Boolean) : string;
var
hs1,hs2 : string;
b : boolean;
op : ttoken;
w : integer;
l1,l2 : longint;
exprType2: TCTETypeSet;
begin
hs1:=read_simple_expr(exprType, eval);
op:=current_scanner.preproc_token;
if (op = _ID) and (current_scanner.preproc_pattern = 'IN') then
op := _IN;
if not (op in [_IN,_EQUAL,_UNEQUAL,_LT,_GT,_LTE,_GTE]) then
begin
read_expr:=hs1;
exit;
end;
if (op = _IN) then
preproc_consume(_ID)
else
preproc_consume(op);
hs2:=read_simple_expr(exprType2, eval);
if eval then
begin
if op = _IN then
begin
if exprType2 <> [ctetSet] then
CTEError(exprType2, [ctetSet], 'IN');
if exprType = [ctetSet] then
CTEError(exprType, setElementTypes, 'IN');
if is_number(hs1) and is_number(hs2) then
Message(scan_e_preproc_syntax_error)
else if hs2[1] = ',' then
b:=pos(','+hs1+',', hs2) > 0 { TODO For integer sets, perhaps check for numeric equivalence so that 0 = 00 }
else
Message(scan_e_preproc_syntax_error);
end
else
begin
if (exprType * exprType2) = [] then
CTEError(exprType2, exprType, tokeninfo^[op].str);
if is_number(hs1) and is_number(hs2) then
begin
val(hs1,l1,w);
val(hs2,l2,w);
case op of
_EQUAL :
b:=l1=l2;
_UNEQUAL :
b:=l1<>l2;
_LT :
b:=l1<l2;
_GT :
b:=l1>l2;
_GTE :
b:=l1>=l2;
_LTE :
b:=l1<=l2;
end;
end
else
begin
case op of
_EQUAL :
b:=hs1=hs2;
_UNEQUAL :
b:=hs1<>hs2;
_LT :
b:=hs1<hs2;
_GT :
b:=hs1>hs2;
_GTE :
b:=hs1>=hs2;
_LTE :
b:=hs1<=hs2;
end;
end;
end;
end
else
b:= false; {Just to have something}
if b then
read_expr:='1'
else
read_expr:='0';
exprType:= [ctetBoolean];
end;
begin
current_scanner.skipspace;
{ start preproc expression scanner }
current_scanner.preproc_token:=current_scanner.readpreproc;
parse_compiler_expr:=read_expr(compileExprType, true);
end;
function boolean_compile_time_expr(var valuedescr: String): Boolean;
var
hs : string;
exprType: TCTETypeSet;
begin
hs:=parse_compiler_expr(exprType);
if (exprType * [ctetBoolean]) = [] then
CTEError(exprType, [ctetBoolean], 'IF or ELSEIF');
boolean_compile_time_expr:= hs <> '0';
valuedescr:= hs;
end;
procedure dir_if;
begin
current_scanner.ifpreprocstack(pp_if,@boolean_compile_time_expr, scan_c_if_found);
end;
procedure dir_elseif;
begin
current_scanner.elseifpreprocstack(@boolean_compile_time_expr);
end;
procedure dir_define_impl(macstyle: boolean);
var
hs : string;
bracketcount : longint;
mac : tmacro;
macropos : longint;
macrobuffer : pmacrobuffer;
begin
current_scanner.skipspace;
hs:=current_scanner.readid;
mac:=tmacro(search_macro(hs));
if not assigned(mac) or (mac.owner <> current_module.localmacrosymtable) then
begin
mac:=tmacro.create(hs);
mac.defined:=true;
Message1(parser_c_macro_defined,mac.name);
current_module.localmacrosymtable.insert(mac);
end
else
begin
Message1(parser_c_macro_defined,mac.name);
mac.defined:=true;
mac.is_compiler_var:=false;
{ delete old definition }
if assigned(mac.buftext) then
begin
freemem(mac.buftext,mac.buflen);
mac.buftext:=nil;
end;
end;
mac.is_used:=true;
if (cs_support_macro in aktmoduleswitches) then
begin
current_scanner.skipspace;
if not macstyle then
begin
{ may be a macro? }
if c <> ':' then
exit;
current_scanner.readchar;
if c <> '=' then
exit;
current_scanner.readchar;
current_scanner.skipspace;
end;
{ key words are never substituted }
if is_keyword(hs) then
Message(scan_e_keyword_cant_be_a_macro);
new(macrobuffer);
macropos:=0;
{ parse macro, brackets are counted so it's possible
to have a $ifdef etc. in the macro }
bracketcount:=0;
repeat
case c of
'}' :
if (bracketcount=0) then
break
else
dec(bracketcount);
'{' :
inc(bracketcount);
#10,#13 :
current_scanner.linebreak;
#26 :
current_scanner.end_of_file;
end;
macrobuffer^[macropos]:=c;
inc(macropos);
if macropos>=maxmacrolen then
Message(scan_f_macro_buffer_overflow);
current_scanner.readchar;
until false;
{ free buffer of macro ?}
if assigned(mac.buftext) then
freemem(mac.buftext,mac.buflen);
{ get new mem }
getmem(mac.buftext,macropos);
mac.buflen:=macropos;
{ copy the text }
move(macrobuffer^,mac.buftext^,macropos);
dispose(macrobuffer);
end
else
begin
{ check if there is an assignment, then we need to give a
warning }
current_scanner.skipspace;
if c=':' then
begin
current_scanner.readchar;
if c='=' then
Message(scan_w_macro_support_turned_off);
end;
end;
end;
procedure dir_define;
begin
dir_define_impl(false);
end;
procedure dir_definec;
begin
dir_define_impl(true);
end;
procedure dir_setc;
var
hs : string;
mac : tmacro;
exprType: TCTETypeSet;
l : longint;
w : integer;
begin
current_scanner.skipspace;
hs:=current_scanner.readid;
mac:=tmacro(search_macro(hs));
if not assigned(mac) or
(mac.owner <> current_module.localmacrosymtable) then
begin
mac:=tmacro.create(hs);
mac.defined:=true;
mac.is_compiler_var:=true;
Message1(parser_c_macro_defined,mac.name);
current_module.localmacrosymtable.insert(mac);
end
else
begin
mac.defined:=true;
mac.is_compiler_var:=true;
{ delete old definition }
if assigned(mac.buftext) then
begin
freemem(mac.buftext,mac.buflen);
mac.buftext:=nil;
end;
end;
mac.is_used:=true;
{ key words are never substituted }
if is_keyword(hs) then
Message(scan_e_keyword_cant_be_a_macro);
{ macro assignment can be both := and = }
current_scanner.skipspace;
if c=':' then
current_scanner.readchar;
if c='=' then
begin
current_scanner.readchar;
hs:= parse_compiler_expr(exprType);
if (exprType * [ctetBoolean, ctetInteger]) = [] then
CTEError(exprType, [ctetBoolean, ctetInteger], 'SETC');
if length(hs) <> 0 then
begin
{If we are absolutely shure it is boolean, translate
to TRUE/FALSE to increase possibility to do future type check}
if exprType = [ctetBoolean] then
begin
val(hs,l,w);
if l<>0 then
hs:='TRUE'
else
hs:='FALSE';
end;
Message2(parser_c_macro_set_to,mac.name,hs);
{ free buffer of macro ?}
if assigned(mac.buftext) then
freemem(mac.buftext,mac.buflen);
{ get new mem }
getmem(mac.buftext,length(hs));
mac.buflen:=length(hs);
{ copy the text }
move(hs[1],mac.buftext^,mac.buflen);
end
else
Message(scan_e_preproc_syntax_error);
end
else
Message(scan_e_preproc_syntax_error);
end;
procedure dir_undef;
var
hs : string;
mac : tmacro;
begin
current_scanner.skipspace;
hs:=current_scanner.readid;
mac:=tmacro(search_macro(hs));
if not assigned(mac) or
(mac.owner <> current_module.localmacrosymtable) then
begin
mac:=tmacro.create(hs);
Message1(parser_c_macro_undefined,mac.name);
mac.defined:=false;
current_module.localmacrosymtable.insert(mac);
end
else
begin
Message1(parser_c_macro_undefined,mac.name);
mac.defined:=false;
mac.is_compiler_var:=false;
{ delete old definition }
if assigned(mac.buftext) then
begin
freemem(mac.buftext,mac.buflen);
mac.buftext:=nil;
end;
end;
mac.is_used:=true;
end;
procedure dir_include;
function findincludefile(const path,name,ext:string;var foundfile:string):boolean;
var
found : boolean;
hpath : string;
begin
(* look for the include file
If path was specified as part of {$I } then
1. specified path (expanded with path of inputfile if relative)
else
1. path of current inputfile,current dir
2. local includepath
3. global includepath *)
found:=false;
foundfile:='';
hpath:='';
if path<>'' then
begin
if not path_absolute(path) then
hpath:=current_scanner.inputfile.path^+path
else
hpath:=path;
found:=FindFile(name+ext, hpath,foundfile);
end
else
begin
hpath:=current_scanner.inputfile.path^+';'+CurDirRelPath(source_info);
found:=FindFile(name+ext, hpath,foundfile);
if not found then
found:=current_module.localincludesearchpath.FindFile(name+ext,foundfile);
if not found then
found:=includesearchpath.FindFile(name+ext,foundfile);
end;
findincludefile:=found;
end;
var
args,
foundfile,
hs : string;
path : dirstr;
name : namestr;
ext : extstr;
hp : tinputfile;
found : boolean;
begin
current_scanner.skipspace;
args:=current_scanner.readcomment;
hs:=GetToken(args,' ');
if hs='' then
exit;
if (hs[1]='%') then
begin
{ case insensitive }
hs:=upper(hs);
{ remove %'s }
Delete(hs,1,1);
if hs[length(hs)]='%' then
Delete(hs,length(hs),1);
{ save old }
path:=hs;
{ first check for internal macros }
if hs='TIME' then
hs:=gettimestr
else
if hs='DATE' then
hs:=getdatestr
else
if hs='FILE' then
hs:=current_module.sourcefiles.get_file_name(aktfilepos.fileindex)
else
if hs='LINE' then
hs:=tostr(aktfilepos.line)
else
if hs='FPCVERSION' then
hs:=version_string
else
if hs='FPCDATE' then
hs:=date_string
else
if hs='FPCTARGET' then
hs:=target_cpu_string
else
if hs='FPCTARGETCPU' then
hs:=target_cpu_string
else
if hs='FPCTARGETOS' then
hs:=target_info.shortname
else
hs:=getenv(hs);
if hs='' then
Message1(scan_w_include_env_not_found,path);
{ make it a stringconst }
hs:=''''+hs+'''';
current_scanner.insertmacro(path,@hs[1],length(hs),
current_scanner.line_no,current_scanner.inputfile.ref_index);
end
else
begin
hs:=FixFileName(hs);
fsplit(hs,path,name,ext);
{ try to find the file }
found:=findincludefile(path,name,ext,foundfile);
if (ext='') then
begin
{ try default extensions .inc , .pp and .pas }
if (not found) then
found:=findincludefile(path,name,'.inc',foundfile);
if (not found) then
found:=findincludefile(path,name,sourceext,foundfile);
if (not found) then
found:=findincludefile(path,name,pasext,foundfile);
end;
if current_scanner.inputfilecount<max_include_nesting then
begin
inc(current_scanner.inputfilecount);
{ we need to reread the current char }
dec(current_scanner.inputpointer);
{ shutdown current file }
current_scanner.tempcloseinputfile;
{ load new file }
hp:=do_openinputfile(foundfile);
current_scanner.addfile(hp);
current_module.sourcefiles.register_file(hp);
if (not found) then
Message1(scan_f_cannot_open_includefile,hs);
if (not current_scanner.openinputfile) then
Message1(scan_f_cannot_open_includefile,hs);
Message1(scan_t_start_include_file,current_scanner.inputfile.path^+current_scanner.inputfile.name^);
current_scanner.reload;
end
else
Message(scan_f_include_deep_ten);
end;
end;
{*****************************************************************************
Preprocessor writting
*****************************************************************************}
{$ifdef PREPROCWRITE}
constructor tpreprocfile.create(const fn:string);
begin
{ open outputfile }
assign(f,fn);
{$I-}
rewrite(f);
{$I+}
if ioresult<>0 then
Comment(V_Fatal,'can''t create file '+fn);
getmem(buf,preprocbufsize);
settextbuf(f,buf^,preprocbufsize);
{ reset }
eolfound:=false;
spacefound:=false;
end;
destructor tpreprocfile.destroy;
begin
close(f);
freemem(buf,preprocbufsize);
end;
procedure tpreprocfile.add(const s:string);
begin
write(f,s);
end;
procedure tpreprocfile.addspace;
begin
if eolfound then
begin
writeln(f,'');
eolfound:=false;
spacefound:=false;
end
else
if spacefound then
begin
write(f,' ');
spacefound:=false;
end;
end;
{$endif PREPROCWRITE}
{*****************************************************************************
TPreProcStack
*****************************************************************************}
constructor tpreprocstack.create(atyp : preproctyp;a:boolean;n:tpreprocstack);
begin
accept:=a;
typ:=atyp;
next:=n;
end;
{*****************************************************************************
TDirectiveItem
*****************************************************************************}
constructor TDirectiveItem.Create(const n:string;p:tdirectiveproc);
begin
inherited CreateName(n);
is_conditional:=false;
proc:=p;
end;
constructor TDirectiveItem.CreateCond(const n:string;p:tdirectiveproc);
begin
inherited CreateName(n);
is_conditional:=true;
proc:=p;
end;
{****************************************************************************
TSCANNERFILE
****************************************************************************}
constructor tscannerfile.create(const fn:string);
begin
inputfile:=do_openinputfile(fn);
if assigned(current_module) then
current_module.sourcefiles.register_file(inputfile);
{ reset localinput }
inputbuffer:=nil;
inputpointer:=nil;
inputstart:=0;
{ reset scanner }
preprocstack:=nil;
comment_level:=0;
yylexcount:=0;
block_type:=bt_general;
line_no:=0;
lastlinepos:=0;
lasttokenpos:=0;
lasttoken:=NOTOKEN;
nexttoken:=NOTOKEN;
lastasmgetchar:=#0;
ignoredirectives:=TStringList.Create;
in_asm_string:=false;
end;
procedure tscannerfile.firstfile;
begin
{ load block }
if not openinputfile then
Message1(scan_f_cannot_open_input,inputfile.name^);
reload;
end;
destructor tscannerfile.destroy;
begin
if assigned(current_module) and
(current_module.state=ms_compiled) and
(status.errorcount=0) then
checkpreprocstack
else
begin
while assigned(preprocstack) do
poppreprocstack;
end;
if not inputfile.closed then
closeinputfile;
ignoredirectives.free;
end;
function tscannerfile.openinputfile:boolean;
begin
openinputfile:=inputfile.open;
{ load buffer }
inputbuffer:=inputfile.buf;
inputpointer:=inputfile.buf;
inputstart:=inputfile.bufstart;
{ line }
line_no:=0;
lastlinepos:=0;
lasttokenpos:=0;
end;
procedure tscannerfile.closeinputfile;
begin
inputfile.close;
{ reset buffer }
inputbuffer:=nil;
inputpointer:=nil;
inputstart:=0;
{ reset line }
line_no:=0;
lastlinepos:=0;
lasttokenpos:=0;
end;
function tscannerfile.tempopeninputfile:boolean;
begin
if inputfile.is_macro then
exit;
tempopeninputfile:=inputfile.tempopen;
{ reload buffer }
inputbuffer:=inputfile.buf;
inputpointer:=inputfile.buf;
inputstart:=inputfile.bufstart;
end;
procedure tscannerfile.tempcloseinputfile;
begin
if inputfile.closed or inputfile.is_macro then
exit;
inputfile.setpos(inputstart+(inputpointer-inputbuffer));
inputfile.tempclose;
{ reset buffer }
inputbuffer:=nil;
inputpointer:=nil;
inputstart:=0;
end;
procedure tscannerfile.saveinputfile;
begin
inputfile.saveinputpointer:=inputpointer;
inputfile.savelastlinepos:=lastlinepos;
inputfile.saveline_no:=line_no;
end;
procedure tscannerfile.restoreinputfile;
begin
inputpointer:=inputfile.saveinputpointer;
lastlinepos:=inputfile.savelastlinepos;
line_no:=inputfile.saveline_no;
if not inputfile.is_macro then
parser_current_file:=inputfile.name^;
end;
procedure tscannerfile.nextfile;
var
to_dispose : tinputfile;
begin
if assigned(inputfile.next) then
begin
if inputfile.is_macro then
to_dispose:=inputfile
else
begin
to_dispose:=nil;
dec(inputfilecount);
end;
{ we can allways close the file, no ? }
inputfile.close;
inputfile:=inputfile.next;
if assigned(to_dispose) then
to_dispose.free;
restoreinputfile;
end;
end;
procedure tscannerfile.startrecordtokens(buf:tdynamicarray);
begin
if not assigned(buf) then
internalerror(200511172);
if assigned(recordtokenbuf) then
internalerror(200511173);
recordtokenbuf:=buf;
end;
procedure tscannerfile.stoprecordtokens;
begin
if not assigned(recordtokenbuf) then
internalerror(200511174);
recordtokenbuf:=nil;
end;
procedure tscannerfile.recordtoken;
begin
if not assigned(recordtokenbuf) then
internalerror(200511176);
recordtokenbuf.write(token,1);
if token=_ID then
recordtokenbuf.write(idtoken,1);
case token of
_CWCHAR,
_CWSTRING :
begin
recordtokenbuf.write(patternw^.len,sizeof(sizeint));
recordtokenbuf.write(patternw^.data^,patternw^.len*sizeof(tcompilerwidechar));
end;
_CCHAR,
_CSTRING,
_INTCONST,
_REALNUMBER :
begin
recordtokenbuf.write(pattern[0],1);
recordtokenbuf.write(pattern[1],length(pattern));
end;
_ID :
begin
recordtokenbuf.write(orgpattern[0],1);
recordtokenbuf.write(orgpattern[1],length(orgpattern));
end;
end;
end;
procedure tscannerfile.startreplaytokens(buf:tdynamicarray);
begin
if not assigned(buf) then
internalerror(200511175);
{ save current token }
if token in [_CWCHAR,_CWSTRING,_CCHAR,_CSTRING,_INTCONST,_REALNUMBER,_ID] then
internalerror(200511178);
replaysavetoken:=token;
dec(inputpointer);
{ install buffer }
replaytokenbuf:=buf;
{ reload next token }
replaytokenbuf.seek(0);
replaytoken;
end;
procedure tscannerfile.replaytoken;
var
wlen : sizeint;
begin
if not assigned(replaytokenbuf) then
internalerror(200511177);
{ End of replay buffer? Then load the next char from the file again }
if replaytokenbuf.pos>=replaytokenbuf.size then
begin
replaytokenbuf:=nil;
c:=inputpointer^;
inc(inputpointer);
token:=replaysavetoken;
exit;
end;
{ load token from the buffer }
replaytokenbuf.read(token,1);
if token=_ID then
replaytokenbuf.read(idtoken,1);
case token of
_CWCHAR,
_CWSTRING :
begin
replaytokenbuf.read(wlen,sizeof(SizeInt));
setlengthwidestring(patternw,wlen);
replaytokenbuf.read(patternw^.data^,patternw^.len*sizeof(tcompilerwidechar));
pattern:='';
end;
_CCHAR,
_CSTRING,
_INTCONST,
_REALNUMBER :
begin
replaytokenbuf.read(pattern[0],1);
replaytokenbuf.read(pattern[1],length(pattern));
orgpattern:='';
end;
_ID :
begin
replaytokenbuf.read(orgpattern[0],1);
replaytokenbuf.read(orgpattern[1],length(orgpattern));
pattern:=upper(orgpattern);
end;
end;
end;
procedure tscannerfile.addfile(hp:tinputfile);
begin
saveinputfile;
{ add to list }
hp.next:=inputfile;
inputfile:=hp;
{ load new inputfile }
restoreinputfile;
end;
procedure tscannerfile.reload;
begin
with inputfile do
begin
{ when nothing more to read then leave immediatly, so we
don't change the aktfilepos and leave it point to the last
char }
if (c=#26) and (not assigned(next)) then
exit;
repeat
{ still more to read?, then change the #0 to a space so its seen
as a seperator, this can't be used for macro's which can change
the place of the #0 in the buffer with tempopen }
if (c=#0) and (bufsize>0) and
not(inputfile.is_macro) and
(inputpointer-inputbuffer<bufsize) then
begin
c:=' ';
inc(inputpointer);
exit;
end;
{ can we read more from this file ? }
if (c<>#26) and (not endoffile) then
begin
readbuf;
inputpointer:=buf;
inputbuffer:=buf;
inputstart:=bufstart;
{ first line? }
if line_no=0 then
begin
c:=inputpointer^;
{ eat utf-8 signature? }
if (ord(inputpointer^)=$ef) and
(ord((inputpointer+1)^)=$bb) and
(ord((inputpointer+2)^)=$bf) then
begin
inc(inputpointer,3);
message(scan_c_switching_to_utf8);
aktsourcecodepage:='utf8';
end;
line_no:=1;
if cs_asm_source in aktglobalswitches then
inputfile.setline(line_no,bufstart);
end;
end
else
begin
{ load eof position in tokenpos/aktfilepos }
gettokenpos;
{ close file }
closeinputfile;
{ no next module, than EOF }
if not assigned(inputfile.next) then
begin
c:=#26;
exit;
end;
{ load next file and reopen it }
nextfile;
tempopeninputfile;
{ status }
Message1(scan_t_back_in,inputfile.name^);
end;
{ load next char }
c:=inputpointer^;
inc(inputpointer);
until c<>#0; { if also end, then reload again }
end;
end;
procedure tscannerfile.insertmacro(const macname:string;p:pchar;len,line,fileindex:longint);
var
hp : tinputfile;
begin
{ save old postion }
dec(inputpointer);
tempcloseinputfile;
{ create macro 'file' }
{ use special name to dispose after !! }
hp:=do_openinputfile('_Macro_.'+macname);
addfile(hp);
with inputfile do
begin
setmacro(p,len);
{ local buffer }
inputbuffer:=buf;
inputpointer:=buf;
inputstart:=bufstart;
ref_index:=fileindex;
end;
{ reset line }
line_no:=line;
lastlinepos:=0;
lasttokenpos:=0;
{ load new c }
c:=inputpointer^;
inc(inputpointer);
end;
procedure tscannerfile.gettokenpos;
{ load the values of tokenpos and lasttokenpos }
begin
lasttokenpos:=inputstart+(inputpointer-inputbuffer);
akttokenpos.line:=line_no;
akttokenpos.column:=lasttokenpos-lastlinepos;
akttokenpos.fileindex:=inputfile.ref_index;
aktfilepos:=akttokenpos;
end;
procedure tscannerfile.inc_comment_level;
var
oldaktfilepos : tfileposinfo;
begin
if (m_nested_comment in aktmodeswitches) then
inc(comment_level)
else
comment_level:=1;
if (comment_level>1) then
begin
oldaktfilepos:=aktfilepos;
gettokenpos; { update for warning }
Message1(scan_w_comment_level,tostr(comment_level));
aktfilepos:=oldaktfilepos;
end;
end;
procedure tscannerfile.dec_comment_level;
begin
if (m_nested_comment in aktmodeswitches) then
dec(comment_level)
else
comment_level:=0;
end;
procedure tscannerfile.linebreak;
var
cur : char;
oldtokenpos,
oldaktfilepos : tfileposinfo;
begin
with inputfile do
begin
if (byte(inputpointer^)=0) and not(endoffile) then
begin
cur:=c;
reload;
if byte(cur)+byte(c)<>23 then
dec(inputpointer);
end
else
begin
{ Support all combination of #10 and #13 as line break }
if (byte(inputpointer^)+byte(c)=23) then
inc(inputpointer);
end;
{ Always return #10 as line break }
c:=#10;
{ increase line counters }
lastlinepos:=bufstart+(inputpointer-inputbuffer);
inc(line_no);
{ update linebuffer }
if cs_asm_source in aktglobalswitches then
inputfile.setline(line_no,lastlinepos);
{ update for status and call the show status routine,
but don't touch aktfilepos ! }
oldaktfilepos:=aktfilepos;
oldtokenpos:=akttokenpos;
gettokenpos; { update for v_status }
inc(status.compiledlines);
ShowStatus;
aktfilepos:=oldaktfilepos;
akttokenpos:=oldtokenpos;
end;
end;
procedure tscannerfile.illegal_char(c:char);
var
s : string;
begin
if c in [#32..#255] then
s:=''''+c+''''
else
s:='#'+tostr(ord(c));
Message2(scan_f_illegal_char,s,'$'+hexstr(ord(c),2));
end;
procedure tscannerfile.end_of_file;
begin
checkpreprocstack;
Message(scan_f_end_of_file);
end;
{-------------------------------------------
IF Conditional Handling
-------------------------------------------}
procedure tscannerfile.checkpreprocstack;
begin
{ check for missing ifdefs }
while assigned(preprocstack) do
begin
Message4(scan_e_endif_expected,preprocstring[preprocstack.typ],preprocstack.name,
preprocstack.owner.inputfile.name^,tostr(preprocstack.line_nb));
poppreprocstack;
end;
end;
procedure tscannerfile.poppreprocstack;
var
hp : tpreprocstack;
begin
if assigned(preprocstack) then
begin
Message1(scan_c_endif_found,preprocstack.name);
hp:=preprocstack.next;
preprocstack.free;
preprocstack:=hp;
end
else
Message(scan_e_endif_without_if);
end;
procedure tscannerfile.ifpreprocstack(atyp : preproctyp;compile_time_predicate:tcompile_time_predicate;messid:longint);
var
condition: Boolean;
valuedescr: String;
begin
if (preprocstack=nil) or preprocstack.accept then
condition:= compile_time_predicate(valuedescr)
else
begin
condition:= false;
valuedescr:= '';
end;
preprocstack:=tpreprocstack.create(atyp, condition, preprocstack);
preprocstack.name:=valuedescr;
preprocstack.line_nb:=line_no;
preprocstack.owner:=self;
if preprocstack.accept then
Message2(messid,preprocstack.name,'accepted')
else
Message2(messid,preprocstack.name,'rejected');
end;
procedure tscannerfile.elsepreprocstack;
begin
if assigned(preprocstack) and
(preprocstack.typ<>pp_else) then
begin
if (preprocstack.typ=pp_elseif) then
preprocstack.accept:=false
else
if (not(assigned(preprocstack.next)) or (preprocstack.next.accept)) then
preprocstack.accept:=not preprocstack.accept;
preprocstack.typ:=pp_else;
preprocstack.line_nb:=line_no;
if preprocstack.accept then
Message2(scan_c_else_found,preprocstack.name,'accepted')
else
Message2(scan_c_else_found,preprocstack.name,'rejected');
end
else
Message(scan_e_endif_without_if);
end;
procedure tscannerfile.elseifpreprocstack(compile_time_predicate:tcompile_time_predicate);
var
valuedescr: String;
begin
if assigned(preprocstack) and
(preprocstack.typ in [pp_if,pp_elseif]) then
begin
{ when the branch is accepted we use pp_elseif so we know that
all the next branches need to be rejected. when this branch is still
not accepted then leave it at pp_if }
if (preprocstack.typ=pp_elseif) then
preprocstack.accept:=false
else if (preprocstack.typ=pp_if) and preprocstack.accept then
begin
preprocstack.accept:=false;
preprocstack.typ:=pp_elseif;
end
else if (not(assigned(preprocstack.next)) or (preprocstack.next.accept))
and compile_time_predicate(valuedescr) then
begin
preprocstack.name:=valuedescr;
preprocstack.accept:=true;
preprocstack.typ:=pp_elseif;
end;
preprocstack.line_nb:=line_no;
if preprocstack.accept then
Message2(scan_c_else_found,preprocstack.name,'accepted')
else
Message2(scan_c_else_found,preprocstack.name,'rejected');
end
else
Message(scan_e_endif_without_if);
end;
procedure tscannerfile.handleconditional(p:tdirectiveitem);
var
oldaktfilepos : tfileposinfo;
begin
oldaktfilepos:=aktfilepos;
repeat
current_scanner.gettokenpos;
p.proc();
{ accept the text ? }
if (current_scanner.preprocstack=nil) or current_scanner.preprocstack.accept then
break
else
begin
current_scanner.gettokenpos;
Message(scan_c_skipping_until);
repeat
current_scanner.skipuntildirective;
if not (m_mac in aktmodeswitches) then
p:=tdirectiveitem(turbo_scannerdirectives.search(current_scanner.readid))
else
p:=tdirectiveitem(mac_scannerdirectives.search(current_scanner.readid));
until assigned(p) and (p.is_conditional);
current_scanner.gettokenpos;
Message1(scan_d_handling_switch,'$'+p.name);
end;
until false;
aktfilepos:=oldaktfilepos;
end;
procedure tscannerfile.handledirectives;
var
t : tdirectiveitem;
hs : string;
begin
gettokenpos;
readchar; {Remove the $}
hs:=readid;
{$ifdef PREPROCWRITE}
if parapreprocess then
begin
t:=Get_Directive(hs);
if not(is_conditional(t) or (t=_DIR_DEFINE) or (t=_DIR_UNDEF)) then
begin
preprocfile^.AddSpace;
preprocfile^.Add('{$'+hs+current_scanner.readcomment+'}');
exit;
end;
end;
{$endif PREPROCWRITE}
{ skip this directive? }
if (ignoredirectives.find(hs)<>nil) then
begin
if (comment_level>0) then
readcomment;
{ we've read the whole comment }
aktcommentstyle:=comment_none;
exit;
end;
if hs='' then
begin
Message1(scan_w_illegal_switch,'$'+hs);
end;
{ Check for compiler switches }
while (length(hs)=1) and (c in ['-','+']) do
begin
HandleSwitch(hs[1],c);
current_scanner.readchar; {Remove + or -}
if c=',' then
begin
current_scanner.readchar; {Remove , }
{ read next switch, support $v+,$+}
hs:=current_scanner.readid;
if (hs='') then
begin
if (c='$') and (m_fpc in aktmodeswitches) then
begin
current_scanner.readchar; { skip $ }
hs:=current_scanner.readid;
end;
if (hs='') then
Message1(scan_w_illegal_directive,'$'+c);
end
else
Message1(scan_d_handling_switch,'$'+hs);
end
else
hs:='';
end;
{ directives may follow switches after a , }
if hs<>'' then
begin
if not (m_mac in aktmodeswitches) then
t:=tdirectiveitem(turbo_scannerdirectives.search(hs))
else
t:=tdirectiveitem(mac_scannerdirectives.search(hs));
if assigned(t) then
begin
if t.is_conditional then
handleconditional(t)
else
begin
Message1(scan_d_handling_switch,'$'+hs);
t.proc();
end;
end
else
begin
current_scanner.ignoredirectives.insert(hs);
Message1(scan_w_illegal_directive,'$'+hs);
end;
{ conditionals already read the comment }
if (current_scanner.comment_level>0) then
current_scanner.readcomment;
{ we've read the whole comment }
aktcommentstyle:=comment_none;
end;
end;
procedure tscannerfile.readchar;
begin
c:=inputpointer^;
if c=#0 then
reload
else
inc(inputpointer);
end;
procedure tscannerfile.readstring;
var
i : longint;
err : boolean;
begin
err:=false;
i:=0;
repeat
case c of
'_',
'0'..'9',
'A'..'Z' :
begin
if i<255 then
begin
inc(i);
orgpattern[i]:=c;
pattern[i]:=c;
end
else
begin
if not err then
begin
Message(scan_e_string_exceeds_255_chars);
err:=true;
end;
end;
c:=inputpointer^;
inc(inputpointer);
end;
'a'..'z' :
begin
if i<255 then
begin
inc(i);
orgpattern[i]:=c;
pattern[i]:=chr(ord(c)-32)
end
else
begin
if not err then
begin
Message(scan_e_string_exceeds_255_chars);
err:=true;
end;
end;
c:=inputpointer^;
inc(inputpointer);
end;
#0 :
reload;
else
break;
end;
until false;
orgpattern[0]:=chr(i);
pattern[0]:=chr(i);
end;
procedure tscannerfile.readnumber;
var
base,
i : longint;
begin
case c of
'%' :
begin
readchar;
base:=2;
pattern[1]:='%';
i:=1;
end;
'&' :
begin
readchar;
base:=8;
pattern[1]:='&';
i:=1;
end;
'$' :
begin
readchar;
base:=16;
pattern[1]:='$';
i:=1;
end;
else
begin
base:=10;
i:=0;
end;
end;
while ((base>=10) and (c in ['0'..'9'])) or
((base=16) and (c in ['A'..'F','a'..'f'])) or
((base=8) and (c in ['0'..'7'])) or
((base=2) and (c in ['0'..'1'])) do
begin
if i<255 then
begin
inc(i);
pattern[i]:=c;
end;
readchar;
end;
pattern[0]:=chr(i);
end;
function tscannerfile.readid:string;
begin
readstring;
readid:=pattern;
end;
function tscannerfile.readval:longint;
var
l : longint;
w : integer;
begin
readnumber;
val(pattern,l,w);
readval:=l;
end;
function tscannerfile.readval_asstring:string;
begin
readnumber;
readval_asstring:=pattern;
end;
function tscannerfile.readcomment:string;
var
i : longint;
begin
i:=0;
repeat
case c of
'{' :
begin
if aktcommentstyle=comment_tp then
inc_comment_level;
end;
'}' :
begin
if aktcommentstyle=comment_tp then
begin
readchar;
dec_comment_level;
if comment_level=0 then
break
else
continue;
end;
end;
'*' :
begin
if aktcommentstyle=comment_oldtp then
begin
readchar;
if c=')' then
begin
readchar;
dec_comment_level;
break;
end
else
{ Add both characters !!}
if (i<255) then
begin
inc(i);
readcomment[i]:='*';
if (i<255) then
begin
inc(i);
readcomment[i]:='*';
end;
end;
end
else
{ Not old TP comment, so add...}
begin
if (i<255) then
begin
inc(i);
readcomment[i]:='*';
end;
end;
end;
#10,#13 :
linebreak;
#26 :
end_of_file;
else
begin
if (i<255) then
begin
inc(i);
readcomment[i]:=c;
end;
end;
end;
readchar;
until false;
readcomment[0]:=chr(i);
end;
function tscannerfile.readquotedstring:string;
var
i : longint;
msgwritten : boolean;
begin
i:=0;
msgwritten:=false;
if (c='''') then
begin
repeat
readchar;
case c of
#26 :
end_of_file;
#10,#13 :
Message(scan_f_string_exceeds_line);
'''' :
begin
readchar;
if c<>'''' then
break;
end;
end;
if i<255 then
begin
inc(i);
result[i]:=c;
end
else
begin
if not msgwritten then
begin
Message(scan_e_string_exceeds_255_chars);
msgwritten:=true;
end;
end;
until false;
end;
result[0]:=chr(i);
end;
function tscannerfile.readstate:char;
var
state : char;
begin
state:=' ';
if c=' ' then
begin
current_scanner.skipspace;
current_scanner.readid;
if pattern='ON' then
state:='+'
else
if pattern='OFF' then
state:='-';
end
else
state:=c;
if not (state in ['+','-']) then
Message(scan_e_wrong_switch_toggle);
readstate:=state;
end;
function tscannerfile.readstatedefault:char;
var
state : char;
begin
state:=' ';
if c=' ' then
begin
current_scanner.skipspace;
current_scanner.readid;
if pattern='ON' then
state:='+'
else
if pattern='OFF' then
state:='-'
else
if pattern='DEFAULT' then
state:='*';
end
else
state:=c;
if not (state in ['+','-','*']) then
Message(scan_e_wrong_switch_toggle_default);
readstatedefault:=state;
end;
procedure tscannerfile.skipspace;
begin
repeat
case c of
#26 :
begin
reload;
if (c=#26) and not assigned(inputfile.next) then
break;
continue;
end;
#10,
#13 :
linebreak;
#9,#11,#12,' ' :
;
else
break;
end;
readchar;
until false;
end;
procedure tscannerfile.skipuntildirective;
var
found : longint;
next_char_loaded : boolean;
begin
found:=0;
next_char_loaded:=false;
repeat
case c of
#10,
#13 :
linebreak;
#26 :
begin
reload;
if (c=#26) and not assigned(inputfile.next) then
end_of_file;
continue;
end;
'{' :
begin
if (aktcommentstyle in [comment_tp,comment_none]) then
begin
aktcommentstyle:=comment_tp;
if (comment_level=0) then
found:=1;
inc_comment_level;
end;
end;
'*' :
begin
if (aktcommentstyle=comment_oldtp) then
begin
readchar;
if c=')' then
begin
dec_comment_level;
found:=0;
aktcommentstyle:=comment_none;
end
else
next_char_loaded:=true;
end
else
found := 0;
end;
'}' :
begin
if (aktcommentstyle=comment_tp) then
begin
dec_comment_level;
if (comment_level=0) then
aktcommentstyle:=comment_none;
found:=0;
end;
end;
'$' :
begin
if found=1 then
found:=2;
end;
'''' :
if (aktcommentstyle=comment_none) then
begin
repeat
readchar;
case c of
#26 :
end_of_file;
#10,#13 :
break;
'''' :
begin
readchar;
if c<>'''' then
begin
next_char_loaded:=true;
break;
end;
end;
end;
until false;
end;
'(' :
begin
if (aktcommentstyle=comment_none) then
begin
readchar;
if c='*' then
begin
readchar;
if c='$' then
begin
found:=2;
inc_comment_level;
aktcommentstyle:=comment_oldtp;
end
else
begin
skipoldtpcomment;
next_char_loaded:=true;
end;
end
else
next_char_loaded:=true;
end
else
found:=0;
end;
'/' :
begin
if (aktcommentstyle=comment_none) then
begin
readchar;
if c='/' then
skipdelphicomment;
next_char_loaded:=true;
end
else
found:=0;
end;
else
found:=0;
end;
if next_char_loaded then
next_char_loaded:=false
else
readchar;
until (found=2);
end;
{****************************************************************************
Comment Handling
****************************************************************************}
procedure tscannerfile.skipcomment;
begin
aktcommentstyle:=comment_tp;
readchar;
inc_comment_level;
{ handle compiler switches }
if (c='$') then
handledirectives;
{ handle_switches can dec comment_level, }
while (comment_level>0) do
begin
case c of
'{' :
inc_comment_level;
'}' :
dec_comment_level;
#10,#13 :
linebreak;
#26 :
begin
reload;
if (c=#26) and not assigned(inputfile.next) then
end_of_file;
continue;
end;
end;
readchar;
end;
aktcommentstyle:=comment_none;
end;
procedure tscannerfile.skipdelphicomment;
begin
aktcommentstyle:=comment_delphi;
inc_comment_level;
readchar;
{ this is not supported }
if c='$' then
Message(scan_w_wrong_styled_switch);
{ skip comment }
while not (c in [#10,#13,#26]) do
readchar;
dec_comment_level;
aktcommentstyle:=comment_none;
end;
procedure tscannerfile.skipoldtpcomment;
var
found : longint;
begin
aktcommentstyle:=comment_oldtp;
inc_comment_level;
{ only load a char if last already processed,
was cause of bug1634 PM }
if c=#0 then
readchar;
{ this is now supported }
if (c='$') then
handledirectives;
{ skip comment }
while (comment_level>0) do
begin
found:=0;
repeat
case c of
#26 :
begin
reload;
if (c=#26) and not assigned(inputfile.next) then
end_of_file;
continue;
end;
#10,#13 :
linebreak;
'*' :
begin
if found=3 then
found:=4
else
found:=1;
end;
')' :
begin
if found in [1,4] then
begin
dec_comment_level;
if comment_level=0 then
found:=2
else
found:=0;
end;
end;
'(' :
begin
if found=4 then
inc_comment_level;
found:=3;
end;
else
begin
if found=4 then
inc_comment_level;
found:=0;
end;
end;
readchar;
until (found=2);
end;
aktcommentstyle:=comment_none;
end;
{****************************************************************************
Token Scanner
****************************************************************************}
procedure tscannerfile.readtoken(allowrecordtoken:boolean);
var
code : integer;
len,
low,high,mid : longint;
w : word;
m : longint;
mac : tmacro;
asciinr : string[6];
msgwritten,
iswidestring : boolean;
label
exit_label;
begin
if localswitcheschanged then
begin
aktlocalswitches:=nextaktlocalswitches;
localswitcheschanged:=false;
end;
{ record tokens? }
if allowrecordtoken and
assigned(recordtokenbuf) then
recordtoken;
{ replay tokens? }
if assigned(replaytokenbuf) then
begin
replaytoken;
goto exit_label;
end;
{ was there already a token read, then return that token }
if nexttoken<>NOTOKEN then
begin
token:=nexttoken;
nexttoken:=NOTOKEN;
goto exit_label;
end;
{ Skip all spaces and comments }
repeat
case c of
'{' :
skipcomment;
#26 :
begin
reload;
if (c=#26) and not assigned(inputfile.next) then
break;
end;
' ',#9..#13 :
begin
{$ifdef PREPROCWRITE}
if parapreprocess then
begin
if c=#10 then
preprocfile.eolfound:=true
else
preprocfile.spacefound:=true;
end;
{$endif PREPROCWRITE}
skipspace;
end
else
break;
end;
until false;
{ Save current token position, for EOF its already loaded }
if c<>#26 then
gettokenpos;
{ Check first for a identifier/keyword, this is 20+% faster (PFV) }
if c in ['A'..'Z','a'..'z','_'] then
begin
readstring;
token:=_ID;
idtoken:=_ID;
{ keyword or any other known token,
pattern is always uppercased }
if (pattern[1]<>'_') and (length(pattern) in [tokenlenmin..tokenlenmax]) then
begin
low:=ord(tokenidx^[length(pattern),pattern[1]].first);
high:=ord(tokenidx^[length(pattern),pattern[1]].last);
while low<high do
begin
mid:=(high+low+1) shr 1;
if pattern<tokeninfo^[ttoken(mid)].str then
high:=mid-1
else
low:=mid;
end;
with tokeninfo^[ttoken(high)] do
if pattern=str then
begin
if keyword in aktmodeswitches then
if op=NOTOKEN then
token:=ttoken(high)
else
token:=op;
idtoken:=ttoken(high);
end;
end;
{ Only process identifiers and not keywords }
if token=_ID then
begin
{ this takes some time ... }
if (cs_support_macro in aktmoduleswitches) then
begin
mac:=tmacro(search_macro(pattern));
if assigned(mac) and (not mac.is_compiler_var) and (assigned(mac.buftext)) then
begin
if yylexcount<max_macro_nesting then
begin
mac.is_used:=true;
inc(yylexcount);
insertmacro(pattern,mac.buftext,mac.buflen,
mac.fileinfo.line,mac.fileinfo.fileindex);
{ handle empty macros }
if c=#0 then
reload;
readtoken(false);
{ that's all folks }
dec(yylexcount);
exit;
end
else
Message(scan_w_macro_too_deep);
end;
end;
end;
{ return token }
goto exit_label;
end
else
begin
idtoken:=_NOID;
case c of
'$' :
begin
readnumber;
token:=_INTCONST;
goto exit_label;
end;
'%' :
begin
if not(m_fpc in aktmodeswitches) then
Illegal_Char(c)
else
begin
readnumber;
token:=_INTCONST;
goto exit_label;
end;
end;
'&' :
begin
if m_fpc in aktmodeswitches then
begin
readnumber;
token:=_INTCONST;
goto exit_label;
end
else if m_mac in aktmodeswitches then
begin
readchar;
token:=_AMPERSAND;
goto exit_label;
end
else
Illegal_Char(c);
end;
'0'..'9' :
begin
readnumber;
if (c in ['.','e','E']) then
begin
{ first check for a . }
if c='.' then
begin
readchar;
{ is it a .. from a range? }
case c of
'.' :
begin
readchar;
token:=_INTCONST;
nexttoken:=_POINTPOINT;
goto exit_label;
end;
')' :
begin
readchar;
token:=_INTCONST;
nexttoken:=_RECKKLAMMER;
goto exit_label;
end;
end;
{ insert the number after the . }
pattern:=pattern+'.';
while c in ['0'..'9'] do
begin
pattern:=pattern+c;
readchar;
end;
end;
{ E can also follow after a point is scanned }
if c in ['e','E'] then
begin
pattern:=pattern+'E';
readchar;
if c in ['-','+'] then
begin
pattern:=pattern+c;
readchar;
end;
if not(c in ['0'..'9']) then
Illegal_Char(c);
while c in ['0'..'9'] do
begin
pattern:=pattern+c;
readchar;
end;
end;
token:=_REALNUMBER;
goto exit_label;
end;
token:=_INTCONST;
goto exit_label;
end;
';' :
begin
readchar;
token:=_SEMICOLON;
goto exit_label;
end;
'[' :
begin
readchar;
token:=_LECKKLAMMER;
goto exit_label;
end;
']' :
begin
readchar;
token:=_RECKKLAMMER;
goto exit_label;
end;
'(' :
begin
readchar;
case c of
'*' :
begin
c:=#0;{Signal skipoldtpcomment to reload a char }
skipoldtpcomment;
readtoken(false);
exit;
end;
'.' :
begin
readchar;
token:=_LECKKLAMMER;
goto exit_label;
end;
end;
token:=_LKLAMMER;
goto exit_label;
end;
')' :
begin
readchar;
token:=_RKLAMMER;
goto exit_label;
end;
'+' :
begin
readchar;
if (c='=') and (cs_support_c_operators in aktmoduleswitches) then
begin
readchar;
token:=_PLUSASN;
goto exit_label;
end;
token:=_PLUS;
goto exit_label;
end;
'-' :
begin
readchar;
if (c='=') and (cs_support_c_operators in aktmoduleswitches) then
begin
readchar;
token:=_MINUSASN;
goto exit_label;
end;
token:=_MINUS;
goto exit_label;
end;
':' :
begin
readchar;
if c='=' then
begin
readchar;
token:=_ASSIGNMENT;
goto exit_label;
end;
token:=_COLON;
goto exit_label;
end;
'*' :
begin
readchar;
if (c='=') and (cs_support_c_operators in aktmoduleswitches) then
begin
readchar;
token:=_STARASN;
end
else
if c='*' then
begin
readchar;
token:=_STARSTAR;
end
else
token:=_STAR;
goto exit_label;
end;
'/' :
begin
readchar;
case c of
'=' :
begin
if (cs_support_c_operators in aktmoduleswitches) then
begin
readchar;
token:=_SLASHASN;
goto exit_label;
end;
end;
'/' :
begin
skipdelphicomment;
readtoken(false);
exit;
end;
end;
token:=_SLASH;
goto exit_label;
end;
'|' :
if m_mac in aktmodeswitches then
begin
readchar;
token:=_PIPE;
goto exit_label;
end
else
Illegal_Char(c);
'=' :
begin
readchar;
token:=_EQUAL;
goto exit_label;
end;
'.' :
begin
readchar;
case c of
'.' :
begin
readchar;
case c of
'.' :
begin
readchar;
token:=_POINTPOINTPOINT;
goto exit_label;
end;
else
begin
token:=_POINTPOINT;
goto exit_label;
end;
end;
end;
')' :
begin
readchar;
token:=_RECKKLAMMER;
goto exit_label;
end;
end;
token:=_POINT;
goto exit_label;
end;
'@' :
begin
readchar;
token:=_KLAMMERAFFE;
goto exit_label;
end;
',' :
begin
readchar;
token:=_COMMA;
goto exit_label;
end;
'''','#','^' :
begin
len:=0;
msgwritten:=false;
pattern:='';
iswidestring:=false;
if c='^' then
begin
readchar;
c:=upcase(c);
if (block_type in [bt_type,bt_specialize]) or
(lasttoken=_ID) or (lasttoken=_NIL) or
(lasttoken=_RKLAMMER) or (lasttoken=_RECKKLAMMER) or (lasttoken=_CARET) then
begin
token:=_CARET;
goto exit_label;
end
else
begin
inc(len);
if c<#64 then
pattern[len]:=chr(ord(c)+64)
else
pattern[len]:=chr(ord(c)-64);
readchar;
end;
end;
repeat
case c of
'#' :
begin
readchar; { read # }
if c='$' then
begin
readchar; { read leading $ }
asciinr:='$';
while (upcase(c) in ['A'..'F','0'..'9']) and (length(asciinr)<6) do
begin
asciinr:=asciinr+c;
readchar;
end;
end
else
begin
asciinr:='';
while (c in ['0'..'9']) and (length(asciinr)<6) do
begin
asciinr:=asciinr+c;
readchar;
end;
end;
val(asciinr,m,code);
if (asciinr='') or (code<>0) then
Message(scan_e_illegal_char_const)
else if (m<0) or (m>255) or (length(asciinr)>3) then
begin
if (m>=0) and (m<=65535) then
begin
if not iswidestring then
begin
ascii2unicode(@pattern[1],len,patternw);
iswidestring:=true;
len:=0;
end;
concatwidestringchar(patternw,tcompilerwidechar(m));
end
else
Message(scan_e_illegal_char_const)
end
else if iswidestring then
concatwidestringchar(patternw,asciichar2unicode(char(m)))
else
begin
if len<255 then
begin
inc(len);
pattern[len]:=chr(m);
end
else
begin
if not msgwritten then
begin
Message(scan_e_string_exceeds_255_chars);
msgwritten:=true;
end;
end;
end;
end;
'''' :
begin
repeat
readchar;
case c of
#26 :
end_of_file;
#10,#13 :
Message(scan_f_string_exceeds_line);
'''' :
begin
readchar;
if c<>'''' then
break;
end;
end;
{ interpret as utf-8 string? }
if (ord(c)>=$80) and (aktsourcecodepage='utf8') then
begin
{ convert existing string to an utf-8 string }
if not iswidestring then
begin
ascii2unicode(@pattern[1],len,patternw);
iswidestring:=true;
len:=0;
end;
{ four or more chars aren't handled }
if (ord(c) and $f0)=$f0 then
message(scan_e_utf8_bigger_than_65535)
{ three chars }
else if (ord(c) and $e0)=$e0 then
begin
w:=ord(c) and $f;
readchar;
if (ord(c) and $c0)<>$80 then
message(scan_e_utf8_malformed);
w:=(w shl 6) or (ord(c) and $3f);
readchar;
if (ord(c) and $c0)<>$80 then
message(scan_e_utf8_malformed);
w:=(w shl 6) or (ord(c) and $3f);
concatwidestringchar(patternw,w);
end
{ two chars }
else if (ord(c) and $c0)<>0 then
begin
w:=ord(c) and $1f;
readchar;
if (ord(c) and $c0)<>$80 then
message(scan_e_utf8_malformed);
w:=(w shl 6) or (ord(c) and $3f);
concatwidestringchar(patternw,w);
end
{ illegal }
else if (ord(c) and $80)<>0 then
message(scan_e_utf8_malformed)
else
concatwidestringchar(patternw,tcompilerwidechar(c))
end
else if iswidestring then
begin
if aktsourcecodepage='utf8' then
concatwidestringchar(patternw,ord(c))
else
concatwidestringchar(patternw,asciichar2unicode(c))
end
else
begin
if len<255 then
begin
inc(len);
pattern[len]:=c;
end
else
begin
if not msgwritten then
begin
Message(scan_e_string_exceeds_255_chars);
msgwritten:=true;
end;
end;
end;
until false;
end;
'^' :
begin
readchar;
c:=upcase(c);
if c<#64 then
c:=chr(ord(c)+64)
else
c:=chr(ord(c)-64);
if iswidestring then
concatwidestringchar(patternw,asciichar2unicode(c))
else
begin
if len<255 then
begin
inc(len);
pattern[len]:=c;
end
else
begin
if not msgwritten then
begin
Message(scan_e_string_exceeds_255_chars);
msgwritten:=true;
end;
end;
end;
readchar;
end;
else
break;
end;
until false;
{ strings with length 1 become const chars }
if iswidestring then
begin
if patternw^.len=1 then
token:=_CWCHAR
else
token:=_CWSTRING;
end
else
begin
pattern[0]:=chr(len);
if len=1 then
token:=_CCHAR
else
token:=_CSTRING;
end;
goto exit_label;
end;
'>' :
begin
readchar;
if (block_type in [bt_type,bt_specialize]) then
token:=_RSHARPBRACKET
else
begin
case c of
'=' :
begin
readchar;
token:=_GTE;
goto exit_label;
end;
'>' :
begin
readchar;
token:=_OP_SHR;
goto exit_label;
end;
'<' :
begin { >< is for a symetric diff for sets }
readchar;
token:=_SYMDIF;
goto exit_label;
end;
end;
token:=_GT;
end;
goto exit_label;
end;
'<' :
begin
readchar;
if (block_type in [bt_type,bt_specialize]) then
token:=_LSHARPBRACKET
else
begin
case c of
'>' :
begin
readchar;
token:=_UNEQUAL;
goto exit_label;
end;
'=' :
begin
readchar;
token:=_LTE;
goto exit_label;
end;
'<' :
begin
readchar;
token:=_OP_SHL;
goto exit_label;
end;
end;
token:=_LT;
end;
goto exit_label;
end;
#26 :
begin
token:=_EOF;
checkpreprocstack;
goto exit_label;
end;
else
Illegal_Char(c);
end;
end;
exit_label:
lasttoken:=token;
end;
function tscannerfile.readpreproc:ttoken;
begin
skipspace;
case c of
'_',
'A'..'Z',
'a'..'z' :
begin
current_scanner.preproc_pattern:=readid;
readpreproc:=_ID;
end;
'0'..'9' :
begin
current_scanner.preproc_pattern:=readval_asstring;
{ realnumber? }
if c='.' then
begin
readchar;
while c in ['0'..'9'] do
begin
current_scanner.preproc_pattern:=current_scanner.preproc_pattern+c;
readchar;
end;
end;
readpreproc:=_ID;
end;
'$','%','&' :
begin
current_scanner.preproc_pattern:=readval_asstring;
readpreproc:=_ID;
end;
',' :
begin
readchar;
readpreproc:=_COMMA;
end;
'}' :
begin
readpreproc:=_END;
end;
'(' :
begin
readchar;
readpreproc:=_LKLAMMER;
end;
')' :
begin
readchar;
readpreproc:=_RKLAMMER;
end;
'[' :
begin
readchar;
readpreproc:=_LECKKLAMMER;
end;
']' :
begin
readchar;
readpreproc:=_RECKKLAMMER;
end;
'+' :
begin
readchar;
readpreproc:=_PLUS;
end;
'-' :
begin
readchar;
readpreproc:=_MINUS;
end;
'*' :
begin
readchar;
readpreproc:=_STAR;
end;
'/' :
begin
readchar;
readpreproc:=_SLASH;
end;
'=' :
begin
readchar;
readpreproc:=_EQUAL;
end;
'>' :
begin
readchar;
if c='=' then
begin
readchar;
readpreproc:=_GTE;
end
else
readpreproc:=_GT;
end;
'<' :
begin
readchar;
case c of
'>' :
begin
readchar;
readpreproc:=_UNEQUAL;
end;
'=' :
begin
readchar;
readpreproc:=_LTE;
end;
else
readpreproc:=_LT;
end;
end;
#26 :
begin
readpreproc:=_EOF;
checkpreprocstack;
end;
else
Illegal_Char(c);
end;
end;
function tscannerfile.asmgetcharstart : char;
begin
{ return first the character already
available in c }
lastasmgetchar:=c;
result:=asmgetchar;
end;
function tscannerfile.asmgetchar : char;
begin
if lastasmgetchar<>#0 then
begin
c:=lastasmgetchar;
lastasmgetchar:=#0;
end
else
readchar;
if in_asm_string then
begin
asmgetchar:=c;
exit;
end;
repeat
case c of
{$ifndef arm}
// the { ... } is used in ARM assembler to define register sets, so we can't used
// it as comment, either (* ... *), /* ... */ or // ... should be used instead
'{' :
skipcomment;
{$endif arm}
#10,#13 :
begin
linebreak;
asmgetchar:=c;
exit;
end;
#26 :
begin
reload;
if (c=#26) and not assigned(inputfile.next) then
end_of_file;
continue;
end;
'/' :
begin
readchar;
if c='/' then
skipdelphicomment
else
begin
asmgetchar:='/';
lastasmgetchar:=c;
exit;
end;
end;
'(' :
begin
readchar;
if c='*' then
begin
c:=#0;{Signal skipoldtpcomment to reload a char }
skipoldtpcomment;
end
else
begin
asmgetchar:='(';
lastasmgetchar:=c;
exit;
end;
end;
else
begin
asmgetchar:=c;
exit;
end;
end;
until false;
end;
{*****************************************************************************
Helpers
*****************************************************************************}
procedure AddDirective(const s:string; dm: tdirectivemode; p:tdirectiveproc);
begin
if dm in [directive_all, directive_turbo] then
turbo_scannerdirectives.insert(tdirectiveitem.create(s,p));
if dm in [directive_all, directive_mac] then
mac_scannerdirectives.insert(tdirectiveitem.create(s,p));
end;
procedure AddConditional(const s:string; dm: tdirectivemode; p:tdirectiveproc);
begin
if dm in [directive_all, directive_turbo] then
turbo_scannerdirectives.insert(tdirectiveitem.createcond(s,p));
if dm in [directive_all, directive_mac] then
mac_scannerdirectives.insert(tdirectiveitem.createcond(s,p));
end;
{*****************************************************************************
Initialization
*****************************************************************************}
procedure InitScanner;
begin
InitWideString(patternw);
turbo_scannerdirectives:=TDictionary.Create;
mac_scannerdirectives:=TDictionary.Create;
{ Common directives and conditionals }
AddDirective('I',directive_all, @dir_include);
AddDirective('DEFINE',directive_all, @dir_define);
AddDirective('UNDEF',directive_all, @dir_undef);
AddConditional('IF',directive_all, @dir_if);
AddConditional('IFDEF',directive_all, @dir_ifdef);
AddConditional('IFNDEF',directive_all, @dir_ifndef);
AddConditional('ELSE',directive_all, @dir_else);
AddConditional('ELSEIF',directive_all, @dir_elseif);
AddConditional('ENDIF',directive_all, @dir_endif);
{ Directives and conditionals for all modes except mode macpas}
AddDirective('INCLUDE',directive_turbo, @dir_include);
AddDirective('LIBPREFIX',directive_turbo, @dir_libprefix);
AddDirective('LIBSUFFIX',directive_turbo, @dir_libsuffix);
AddDirective('EXTENSION',directive_turbo, @dir_extension);
AddConditional('IFEND',directive_turbo, @dir_endif);
AddConditional('IFOPT',directive_turbo, @dir_ifopt);
{ Directives and conditionals for mode macpas: }
AddDirective('SETC',directive_mac, @dir_setc);
AddDirective('DEFINEC',directive_mac, @dir_definec);
AddDirective('UNDEFC',directive_mac, @dir_undef);
AddConditional('IFC',directive_mac, @dir_if);
AddConditional('ELSEC',directive_mac, @dir_else);
AddConditional('ELIFC',directive_mac, @dir_elseif);
AddConditional('ENDC',directive_mac, @dir_endif);
end;
procedure DoneScanner;
begin
turbo_scannerdirectives.Free;
mac_scannerdirectives.Free;
DoneWideString(patternw);
end;
end.