lazarus-ccr/components/chelper/cparsertypes.pas

2106 lines
50 KiB
ObjectPascal
Executable File

{ The unit is part of Lazarus Chelper package
Copyright (C) 2010 Dmitry Boyarintsev skalogryz dot lists at gmail.com
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library 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 Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}
unit cparsertypes;
interface
{$ifdef fpc}{$mode delphi}{$h+}{$endif}
uses
Classes, SysUtils, TextParsingUtils;
const
Err_Ident = 'Identifier';
Err_Expect = '%s, excepted, but "%s" found';
Err_BadPrecompile = 'Bad precompile directive';
type
TTokenType = (tt_Ident, tt_Symbol, tt_Numeric, tt_String);
TTokenPair = record
Open : AnsiString;
Close : AnsiString;
end;
{ TTokenTable }
TTokenTable = class(TObject)
private
fSymbMaxLen : Integer;
fSymbStrs : TStringList;
public
SpaceChars : TCharSet;
CmtBlock : array of TTokenPair;
CmtCount : Integer;
CmtLine : TStrings;
StringStart : TCharSet;
Symbols : TCharSet;
Precompile : AnsiString;
MultiLine : AnsiChar;
constructor Create;
destructor Destroy; override;
function AddSymbol(const asym: AnsiString): Boolean;
function isSymbol(const asym: AnsiSTring): Boolean;
property SymbMaxLen : Integer read fSymbMaxLen;
end;
TTextParser = class;
TPrecompilerEvent = procedure (Sender: TTextParser; PrecompEntity: TObject) of object;
TCMacroStruct = class(TObject)
MacroName : AnsiString;
MacroParams : TStringList;
ReplaceText : AnsiString;
constructor Create;
destructor Destroy; override;
end;
{ TCMacroHandler }
TCMacroHandler = class(TObject)
public
MacrosNames : TStringList;
constructor Create;
destructor Destroy; override;
function ParseMacro(const Parser: TTextParser; var MacroStr, ReplaceStr: AnsiString): Boolean;
function isMacroDefined(const Macro: AnsisTring): Boolean;
procedure AddSimpleMacro(const MacroStr, ReplaceStr: AnsiString);
procedure AddParamMacro(const MacroStr, ReplaceStr: AnsiString; Params: TStrings);
procedure Clear;
end;
{ TTextParser }
TTextParser = class(TObject)
protected
ProcessingMacro : Boolean;
function HandlePrecomiler: Boolean; virtual;
function HandleMacro(var MacroStr: AnsiString; var ReplaceStr: AnsiString): Boolean;
function IsMultiLine: Boolean;
procedure SkipSingleEoLnChars;
public
Buf : AnsiString;
Token : AnsiString;
TokenType : TTokenType;
TokenCode : Integer; // code for reserved tokens and symbols, otherwiser -1. 0 is EOF
Index : Integer; // current index where text parsing goes on
TokenPos : Integer; // position of currently found token by (FindTextToken)
MacrosDelta : Integer; // the difference between Buf Index and Original Text index, caused by Macros substitution
TokenTable : TTokenTable;
OnPrecompile : TPrecompilerEvent;
OnComment : procedure (Sender: TObject; const Comment: AnsiString) of object;
OnIgnoreToken : procedure (Sender: TObject; const Ignored: AnsiString) of object;
Line : Integer;
Stack : TList;
Errors : TStringList;
MacroHandler : TCMacroHandler;
UseCommentEntities : Boolean;
UsePrecompileEntities : Boolean;
Comments : TList;
constructor Create;
destructor Destroy; override;
procedure BeginParse(AObject: TObject);
procedure EndParse;
function GetBufWideStr(const Cmd: AnsiString): WideString;
function SkipComments: Boolean;
function NextToken: Boolean;
function FindNextToken(var AToken: AnsiString; var ATokenType: TTokenType): Boolean;
procedure SetError(const ErrorCmt: AnsiString);
end;
{ TEntity }
TEntity = class(TObject)
protected
function DoParse(AParser: TTextParser): Boolean; virtual;
public
Offset : Integer;
Specifiers : TStringList;
constructor Create(AOffset: Integer=-1); virtual;
destructor Destroy; override;
function Parse(AParser: TTextParser): Boolean; virtual;
end;
TEntityClass = class of TEntity;
TCPrepocessor = class(TEntity);
{ TCPrepDefine }
TCPrepDefine = class(TCPrepocessor)
protected
function DoParse(AParser: TTextParser): Boolean; override;
public
Params : TStringList;
_Name : AnsiString;
SubsText : AnsiString;
destructor Destroy; override;
end;
{ TCPrepInclude }
TCPrepInclude = class(TCPrepocessor)
protected
function DoParse(AParser: TTextParser): Boolean; override;
public
Params : TStringList;
Included : AnsiString;
destructor Destroy; override;
end;
TCPrepElse = class(TCPrepocessor)
function DoParse(AParser: TTextParser): Boolean; override;
end;
TCPrepEndif = class(TCPrepocessor)
function DoParse(AParser: TTextParser): Boolean; override;
end;
TCPrepIf = class(TCPrepocessor)
_Cond : AnsiString;
IfOp : AnsiString;
function DoParse(AParser: TTextParser): Boolean; override;
end;
TCPrepPragma = class(TCPrepocessor)
_Text : AnsiString;
function DoParse(AParser: TTextParser): Boolean; override;
end;
//C tokens: /*, //
TCommentType = (ctLine, ctBlock);
{ TComment }
TComment = class(TEntity)
protected
function DoParse(AParser: TTextParser): Boolean; override;
public
CommenType : TCommentType;
_Comment : AnsiString; // in case sources are UTF8 or Unicode
end;
{ TPrecompiler }
TPrecompiler = class(TEntity)
protected
function DoParse(AParser: TTextParser): Boolean; override;
public
_Directive : AnsiString;
_Params : AnsiString;
end;
type
{ TSimpleType }
TSimpleType = class(TEntity)
public
Name : AnsiString;
end;
{ TExpression }
TExpPart = record
Token : AnsiString;
TokenType : TTokenType;
end;
TExpression = class(TEntity)
public
Tokens : array of TExpPart;
Count : Integer;
procedure PushToken(const AToken: AnsiString; ATokenType: TTokenType);
end;
const
nk_Ident = 0;
nk_Ref = 1;
nk_Array = 2;
nk_Func = 3;
nk_Block = 4;
type
TNameKind = Integer;
type
TNamePart = class;
TFuncParam = record
prmtype : TEntity;
name : TNamePart;
end;
{ TNamePart }
TNamePart = class(TObject)
private
fChild : TNamePart;
fOwner : TNamePart;
public
Kind : TNameKind;
RefCount : Integer;
Id : AnsiString;
arrayexp : array of TExpression;
params : array of TFuncParam;
constructor Create(AKind: TNameKind);
destructor Destroy; override;
procedure AddParam(prmtype: TEntity; prmname: TNamePart);
procedure AddArrayExpr(expr: TExpression);
property child: TNamePart read fchild write fChild; // int (*p)[10]; "[10]" is child of (*p)
property owner: TNamePart read fowner write fOwner;
end;
var
ParseNextEntity: function (AParser: TTextParser): TEntity = nil;
ParseNamePart: function (Parser: TTextParser): TNamePart = nil;
ParsePreproc: function (AParser: TTextParser): TEntity = nil;
function ParseNextCEntity(AParser: TTextParser): TEntity; // default ParseNextEntity
function ParseCNamePart(Parser: TTextParser): TNamePart; // default ParseNamePart
function ParseCExpression(AParser: TTextParser; var ExpS: AnsiString): Boolean;
procedure ParseCNumeric(const S: AnsiString; var idx: integer; var NumStr: AnsiSTring);
function ParseCString(const S: AnsiString; var idx: Integer; var CStr: AnsiString): Boolean;
function ParseCMacroParam(AParser: TTextParser; var ExpS: AnsiString): Boolean;
// utility function
function SkipEndOfLineChars(const Src: AnsiString; idx: integer): Integer;
function CToPascalNumeric(const Cnum: AnsiString): AnsiString;
function ErrExpectStr(const Expected, Found: AnsiString): AnsiString;
// Parser data management functions
function CreateObjCTokenTable: TTokenTable;
procedure SetCComments(Table: TTokenTable);
procedure SetCSymbols(var ch: TCharSet);
function CreateCParser(const CHeaderText: AnsiString;
WithCMacroHandler: Boolean = false): TTextParser;
type
TCustomEntityProc = function (Parent: TEntity; Parser: TTextParser): TEntity;
procedure ErrorExpect(Parser: TTextParser; const Expect: AnsiString);
function ConsumeToken(Parser: TTextParser; const Token: AnsiString): Boolean;
function ConsumeIdentifier(Parser: TTextParser; var Id: AnsiString): Boolean;
function ParseCType(Parser: TTextParser): TEntity;
function ParseNames(Parser: TTextParser; var NameType: TEntity; Names: TList; const EndChars: TCharSet; AllowMultipleNames: Boolean=True): Boolean;
function ParseName(Parser: TTextParser; var NameType: TEntity; var name: TNamePart; const EndChars: TCharSet): Boolean;
type
{ TVarFuncEntity }
TVarFuncEntity = class(TEntity)
public
RetType : TEntity;
Names : TList;
constructor Create(AOffset: Integer=-1); override;
destructor Destroy; override;
function FirstName: TNamePart;
end;
TStructTypeField = record
v : TVarFuncEntity;
isbitted : Boolean;
bits : TExpression;
end;
{ TStructType }
TStructType = class(TEntity)
public
Name : AnsiString;
fields : array oF TStructTypeField;
destructor Destroy; override;
function AddField(ev: TVarFuncEntity): Integer;
end;
{ TUnionType }
TUnionType = class(TEntity)
public
Name : AnsiString;
fields : array oF TStructTypeField;
destructor Destroy; override;
function AddField(ev: TVarFuncEntity): Integer;
end;
{ TTypeDefInst }
TTypeDef = class(TEntity)
public
origintype : TEntity;
names : TList;
constructor Create(AOffset: Integer=-1); override;
destructor Destroy; override;
end;
TEnumItem = record
Name : AnsiString;
Value : TExpression;
Offset : Integer;
end;
{ TEnumType }
TEnumType = class(TEntity)
Name : AnsiString;
items : array of TEnumItem;
function AddItem(const name: AnsiString; x: TExpression; Offset: Integer = -1): Integer;
end;
function ParseStruct(AParser: TTextParser): TStructType;
function ParseUnion(AParser: TTextParser): TUnionType;
function ParseTypeDef(AParser: TTextParser): TTypeDef;
function ParseEnum(AParser: TTextParser): TEnumType;
implementation
function SkipEndOfLineChars(const Src: AnsiString; idx: integer): Integer;
begin
if idx < length(Src) then begin
if (Src[idx] = #10) and (Src[idx+1]=#13) then inc(idx)
else if (Src[idx] = #13) and (Src[idx+1]=#10) then inc(idx);
end;
Result := idx+1;
end;
function CreateCParser(const CHeaderText: AnsiString; WithCMacroHandler: Boolean): TTextParser;
begin
Result := TTextParser.Create;
Result.TokenTable := CreateObjCTokenTable;
if WithCMacroHandler then
Result.MacroHandler := TCMacroHandler.Create;
Result.Buf := CHeaderText;
end;
function ErrExpectStr(const Expected, Found: AnsiString): AnsiString;
begin
Result := Format(Err_Expect, [Expected, Found]);
end;
(* ANSI C reserved words
auto break case char const continue default do double else enum
extern float for goto if int long register return short signed
sizeof static struct switch typedef union unsigned void volatile while
*)
function CreateObjCTokenTable: TTokenTable;
begin
Result := TTokenTable.Create;
SetCComments(Result);
SetCSymbols(Result.Symbols);
Result.AddSymbol('!=');
Result.AddSymbol('==');
Result.AddSymbol('+=');
Result.AddSymbol('-=');
Result.AddSymbol('*=');
Result.AddSymbol('/=');
Result.AddSymbol('%=');
Result.AddSymbol('|=');
Result.AddSymbol('&=');
Result.AddSymbol('<<');
Result.AddSymbol('>>');
Result.AddSymbol('++');
Result.AddSymbol('--');
Result.AddSymbol('||');
Result.AddSymbol('&&');
Result.SpaceChars := EoLnChars + InvsChars;
Result.Precompile := '#';
Result.MultiLine := '\';
Result.StringStart := ['"', #39];
end;
procedure SetCSymbols(var ch: TCharSet);
begin
ch := ['!','~','^','(',')','{','}','%','/',':','=','-','+','<','>','*',';', ',','|','&','[',']'{, #39 ,'"'} ]
end;
procedure SetCComments(Table: TTokenTable);
begin
SetLength(Table.CmtBlock, 1);
Table.CmtCount := 1;
Table.CmtBlock[0].Open := '/*';
Table.CmtBlock[0].Close := '*/';
Table.CmtLine.Add('//');
end;
function isFloatNum(const num: AnsiString): Boolean;
begin
Result := Pos('.', num)>0;
end;
function ParseHexNumber(const S:AnsiString; var idx: Integer): AnsiString;
begin
Result := ScanWhile(s, idx, ['0'..'9', 'A'..'F', 'a'..'f']);
end;
procedure ParseCNumeric(const S: AnsiString; var idx: integer; var NumStr: AnsiSTring);
var
l : integer;
i : Integer;
f : AnsiString;
begin
l := length(s);
if (idx <= 0) or (idx > l) then Exit;
if (s[idx] = '0') and (idx < l) and ((s[idx+1] = 'x') or (s[idx+1] = 'X')) then begin
inc(idx,2);
NumStr := '0x'+ParseHexNumber(s, idx);
end else begin
NumStr := ScanWhile(s, idx, ['0'..'9']);
if (idx < l) and (s[idx] = '.') then begin
i := idx + 1;
f := ScanWhile(s, i, ['0'..'9']);
if f <> '' then begin
idx := i;
NumStr := NumStr + '.' + f;
end;
end;
end;
ScanWhile(s, idx, ['U','L','u','l']);
end;
function CToPascalNumeric(const Cnum: AnsiString): AnsiString;
var
i : Integer;
num : Int64;
c : Int64;
begin
if isFloatNum(cNum) then
Result := cNum
else if length(cNum) < 3 then
Result := cNum
else if cNum[1] <> '0' then
Result := cNum
else begin
if cNum[2] = 'x'
then Result := '$'+Copy(cNum, 3, length(cNum) - 2)
else begin
num := 0;
c := 1;
for i := length(cnum) downto 1 do begin
if not (cnum[i] in['0'..'7']) then begin
Result := cNum;
Exit;
end;
num := num + c * (byte(cnum[i]) - byte('0'));
c := c * 8;
end;
Result := IntToStr(num);
end;
end;
end;
function ParseCString(const S: AnsiString; var idx: Integer; var CStr: AnsiString): Boolean;
var
quit : Boolean;
i : Integer;
ch : AnsiChar;
begin
Result := false;
CStr := '';
if not (S[idx] in ['"', #39]) then Exit;
quit := false;
i := idx+1;
ch := S[idx];
while (not quit) and (i <= length(s)) do begin
ScanTo(s, i, [ch, #10, #13] );
quit := (i > length(s)) or (s[i] in [ch, #10, #13]);
if quit and (i <= length(s)) and ((s[i] ='"')) then
if ((s[i] = ch) and (s[i-1] = '\')) then begin
inc(i);
quit := false;
end;
end;
Result := (i <= length(s)) and (s[i] = ch);
if Result then begin
inc(i);
CStr := Copy(s, idx, i-idx);
idx := i;
end;
end;
function ParseCOperator(AParser: TTextParser; var Vl: AnsiString): Boolean;
var
nm : AnsiSTring;
tt : TTokenType;
begin
Result := false;
if not AParser.FindNextToken(nm, tt) then Exit;
Result := nm <> '';
if not Result then Exit;
vl := nm[1];
case vl[1] of
'+', '-', '*': Result := true;
'|', '&': begin
Result := true;
end;
'<', '>': begin
vl := nm[1];
Result := AParser.FindNextToken(nm, tt);
if (not Result) or (nm = '') then Exit;
Result := nm[1] = vl[1] ;
if Result then vl := vl[1] + nm[1];
end;
else
Result := false;
end;
end;
function ParseCMacroParam(AParser: TTextParser; var ExpS: AnsiString): Boolean;
var
brac : Integer;
idx : Integer;
begin
idx := AParser.Index;
brac:=0;
while AParser.NextToken do begin
if AParser.Token='(' then inc(brac)
else if (AParser.Token=')') then begin
if brac>0 then dec(brac)
else begin
AParser.Index:=aParser.TokenPos;
Break;
end;
end else if (AParser.Token=',') and (brac=0) then begin
AParser.Index:=AParser.TokenPos;
Break;
end;
end;
ExpS:=Copy(APArser.Buf, idx, AParser.Index-idx);
Result:=True;
end;
function ParseCExpression(AParser: TTextParser; var ExpS: AnsiString): Boolean;
var
i : integer;
nm : AnsiString;
tt : TTokenType;
brac : Integer;
begin
//todo: better code. it's just a work around
// i := AParser.Index;
brac := 0;
ExpS := '';
Result := false;
try
while AParser.FindNextToken(nm, tt) do begin
if (nm = #39) then begin
ExpS := #39 + ScanTo(AParser.Buf, AParser.Index, [#39]) + #39;
inc(AParser.Index);
Result := true;
Exit;
end else if (tt = tt_Numeric) or (tt = tt_Ident) then begin
ExpS := ExpS + nm;
i := AParser.Index;
if not ParseCOperator(AParser, nm) then begin
AParser.Index := i;
Break;
end else
ExpS := ExpS + ' ' + nm + ' ';
end else if (tt = tt_Symbol) then begin
if nm ='(' then inc(brac)
else if (nm = ')') then begin
if brac=0 then dec(brac)
else begin
AParser.Index:=AParser.TokenPos;
Break;
end;
end;
end else begin
//i := AParser.Index;
Exit;
end;
end;
Result := true;
finally
while (brac > 0) and (AParser.FindNextToken(nm, tt)) do
if nm = ')' then
dec(brac);
end;
end;
{ TTextParser }
constructor TTextParser.Create;
begin
Index := 1;
Line := 1;
Stack := TList.Create;
Errors := TStringList.Create;
//IgnoreTokens := TStringList.Create;
UsePrecompileEntities := true;
Comments := TList.Create;
end;
destructor TTextParser.Destroy;
var
i : Integer;
begin
TokenTable.Free;
for i:=0 to Comments.Count-1 do
TObject(Comments[i]).Free;
Comments.Free;
//IgnoreTokens.Free;
Errors.Free;
Stack.Free;
inherited Destroy;
end;
procedure TTextParser.BeginParse(AObject: TObject);
begin
Stack.Add(AObject);
end;
procedure TTextParser.EndParse;
begin
if Stack.Count > 0 then Stack.Delete(Stack.Count - 1);
end;
function TTextParser.HandlePrecomiler: Boolean;
var
idx : Integer;
s : AnsiString;
df : TCPrepocessor;
i : integer;
begin
Result := false;
if ProcessingMacro then Exit;
ProcessingMacro := true;
try
idx := Index;
i := idx+1;
ScanWhile(Buf, i, WhiteSpaceChars);
s := ScanTo(Buf, i, SpaceEolnChars);
if s='define' then df := TCPrepDefine.Create(idx)
else if (s='include') or (s='import') then df := TCPrepInclude.Create(idx)
else if s='else' then df := TCPrepInclude.Create(idx)
else if s='endif' then df := TCPrepEndif.Create(idx)
else if s='pragma' then df := TCPrepPragma.Create(idx)
else if (s='if') or (s='elif') or (s='ifdef') or (s='ifndef') then begin
df := TCPrepIf.Create(idx);
TCPrepIf(df).IfOp:=s;
end else
df := nil;
Result := Assigned(df);
if Result then begin
Index:=i;
Result := df.Parse(Self);
Comments.Add(df);
if Assigned(OnPrecompile) then OnPrecompile(Self, df);
end else
SetError('cannot handle preprocessor: "'+s+'"');
finally
ProcessingMacro := false;
end;
end;
function TTextParser.FindNextToken(var AToken: AnsiString; var ATokenType: TTokenType): Boolean;
begin
Result:=NextToken;
AToken:=Token;
ATokenType:=TokenType;
end;
function TTextParser.SkipComments: Boolean;
var
i : Integer;
idx : Integer;
cmt : AnsiString;
comment : TComment;
ct : TCommentType;
begin
cmt := '';
Result := false;
for i := 0 to TokenTable.CmtCount - 1 do begin
Result:=IsSubStr(TokenTable.CmtBlock[i].Open, Buf, index);
if Result then begin
idx:=index;
inc(index, length(TokenTable.CmtBlock[i].Open));
cmt := SkipCommentBlock(Buf, index, TokenTable.CmtBlock[i].Close);
ct:=ctBlock;
Break;
end;
end;
if not Result then begin
for i := 0 to TokenTable.CmtLine.Count - 1 do begin
Result:=IsSubStr(TokenTable.CmtLine[i], Buf, index);
if Result then begin
idx:=index;
cmt := SkipLine(Buf, index);
Delete(cmt, 1, length(TokenTable.CmtLine[i]) );
ct:=ctLine;
Break;
end;
end;
end;
if Result then begin
if UseCommentEntities then begin
comment := TComment.Create(idx);
comment._Comment := cmt;
comment.CommenType:=ct;
Comments.Add(Comment);
end;
if (Assigned(OnComment)) then OnComment(Self, cmt);
end;
end;
function TTextParser.NextToken:Boolean;
var
srch : TCharSet;
blck : TCharSet;
i, j : Integer;
t : AnsiString;
spaces : TCharSet;
Repl : AnsiString;
p : Integer;
begin
Result := Index <= length(Buf);
if not Result then begin
Token:='';
Exit;
end;
srch := TokenTable.SpaceChars;
blck := [];
for i := 0 to TokenTable.CmtCount - 1 do begin
t := TokenTable.CmtBlock[i].Open[1];
if t <> '' then blck := blck + [t[1]];
end;
for i := 0 to TokenTable.CmtLine.Count - 1 do begin
t := TokenTable.CmtLine[i];
if t <> '' then blck := blck + [t[1]];
end;
srch := srch + blck;
Token := '';
Result := false;
TokenType := tt_Ident;
spaces := TokenTable.SpaceChars;
try
while (not Result) and (index <= length(Buf)) do begin
ScanWhile(Buf, index, spaces);
if isMultiline then begin
ScanTo(Buf, index, EoLnChars);
SkipSingleEoLnChars;
end else begin
if (IsSubStr(TokenTable.Precompile, Buf, Index) and HandlePrecomiler) then begin
Token:='';
TokenType:=tt_Ident;
// 1. check is Preprocessor directive is found
end else if (Buf[index] in TokenTable.Symbols) then begin // 2. symbol has been found, so it's not an ident
if (not (Buf[index] in blck)) or (not SkipComments) then begin // 2.1 check if comment is found (comment prefixes match to the symbols)
Result := true; // 2.2 check if symbol is found
if (Buf[index] = '.') and (index < length(Buf)) and (Buf[index+1] in ['0'..'9']) then begin
// is float number
inc(index);
Token := '.' + ScanWhile(Buf, index, ['0'..'9']);
TokenType := tt_Numeric;
end else begin
j:=index;
//todo: improve!
while (j-index<=TokenTable.SymbMaxLen) and (Buf[j] in (TokenTable.Symbols)) do inc(j);
if TokenTable.isSymbol( Copy( buf, index, j-index) ) then begin
Token:=Copy( buf, index, j-index);
index:=j;
end else begin
Token := Buf[index];
inc(index);
end;
TokenType := tt_Symbol;
end;
Exit;
end;
end else if (Buf[index] in ['0'..'9']) then begin // 3. a number is found, so it's possibl a number
//todo: Hex and floats support!
//todo: Negative numbers support;
ParseCNumeric(Buf, index, Token);
TokenType := tt_Numeric;
Result := true;
Exit;
end else if (Buf[index] in TokenTable.StringStart) then begin
ParseCString(Buf, index, Token);
TokenType := tt_String;
Result := true;
Exit;
end else begin
Token := Token + ScanTo(Buf, index, srch+TokenTable.Symbols+[TokenTable.MultiLine]); // scanning for token
if (Buf[index] in blck) then begin
Result := SkipComments;
Result := Result or (Buf[index] in TokenTable.SpaceChars);
if not Result then begin
Token := Token + Buf[index];
inc(index);
end;
end else
Result := true;
Result := Result and (Token <> '');
end;
end;
if (Token <> '') and (TokenType = tt_Ident) and Result then begin
p := Index - length(Token);
TokenPos:=p;
if HandleMacro(Token, Repl) then begin
inc(MacrosDelta, length(Token)-length(Repl));
Delete(buf, p, length(Token));
Insert(Repl, Buf, p);
Index := p;
Result := false;
TokenType := tt_Ident;
Token := '';
end else
TokenPos:=p;
end;
end; {of while}
finally
if not Result
then TokenType := tt_Ident
else TokenPos := Index - length(Token);
//todo: make an event or something
if TokenType = tt_Numeric then
Token := CToPascalNumeric(Token);
end;
end;
procedure TTextParser.SetError(const ErrorCmt: AnsiString);
begin
Errors.Add(ErrorCmt);
end;
function TTextParser.HandleMacro(var MacroStr: AnsiString; var ReplaceStr: AnsiString): Boolean;
begin
Result := false;
if ProcessingMacro or not Assigned(MacroHandler) then Exit;
ProcessingMacro := true;
try
Result := MacroHandler.isMacroDefined(MacroStr);
if not Result then Exit;
Index := TokenPos;
Result := MacroHandler.ParseMacro(Self, MacroStr, ReplaceStr);
finally
ProcessingMacro := false;
end;
end;
function TTextParser.GetBufWideStr(const Cmd: AnsiString): WideString;
begin
Result := Cmd;
end;
function TTextParser.IsMultiLine: Boolean;
begin
Result := TokenTable.MultiLine <> #0;
if not Result then Exit;
Result := (Buf[index] = TokenTable.MultiLine);
end;
procedure TTextParser.SkipSingleEoLnChars;
var
next : integer;
begin
next := index + 1;
if next > length(Buf) then next := -1;
if next < 0 then
inc(index)
else begin
if (Buf[index] = #10) and (Buf[next] = #13) then
Index := next+1
else if (Buf[index] = #13) and (Buf[next] = #10) then
Index := next + 1
else
inc(Index);
end;
end;
{ TTokenTable }
constructor TTokenTable.Create;
begin
CmtLine:=TStringList.Create;
fSymbStrs:=TStringList.Create;
end;
destructor TTokenTable.Destroy;
begin
fSymbStrs.Free;
CmtLine.Free;
inherited;
end;
function TTokenTable.AddSymbol(const asym:AnsiString):Boolean;
begin
Result:=False;
if asym='' then Exit;
fSymbStrs.Add(asym);
if length(asym)>fSymbMaxLen then fSymbMaxLen:=length(asym);
end;
function TTokenTable.isSymbol(const asym:AnsiSTring):Boolean;
begin
if asym='' then
Result:=false
else begin
if length(asym)=1 then
Result:=(asym[1] in Symbols) or (fSymbStrs.IndexOf(asym)>=0)
else
Result:=fSymbStrs.IndexOf(asym)>=0;
end;
end;
{ TEntity }
function TEntity.DoParse(AParser:TTextParser):Boolean;
begin
Result:=False;
end;
constructor TEntity.Create(AOffset: Integer);
begin
inherited Create;
Offset := AOffset;
Specifiers := TStringList.create;
end;
destructor TEntity.Destroy;
begin
Specifiers.Free;
inherited Destroy;
end;
function TEntity.Parse(AParser: TTextParser): Boolean;
begin
Result := false;
AParser.BeginParse(Self);
try
Result := DoParse(AParser);
except
on e: Exception do
AParser.SetError('Internal error. Exception: ' + e.Message);
end;
AParser.EndParse;
end;
{ TPrecompiler }
function TPrecompiler.DoParse(AParser: TTextParser): Boolean;
var
tt : TTokenType;
begin
Result := false;
if not AParser.FindNextToken(_Directive, tt) then begin
AParser.SetError('precompiler directive not found');
Exit;
end;
if (_Directive = '') or (_Directive[1] <> '#') then begin
AParser.Index := AParser.TokenPos;
AParser.SetError('identifier is not precompiler directive');
Exit;
end;
_Params := SkipLine(AParser.Buf, AParser.Index);
Result := true;
end;
{ TComment }
function TComment.DoParse(AParser: TTextParser): Boolean;
begin
Result := true;
end;
function RemoveMacroSlash(const macro: AnsiString): AnsiString;
var
i : integer;
begin
for i := length(macro) downto 1 do
if not (macro[i] in WhiteSpaceChars) then begin
if macro[i] = '\' then Result := Copy(macro, 1, i-1);
Exit;
end;
Result := macro;
end;
function ConsumePreproc(AParser: TTextParser; const preprocname: AnsiString): Boolean;
begin
AParser.NextToken;
Result:=AParser.Token='#'+preprocname;
if Result then Exit
else begin
if AParser.Token<>'#' then Exit;
AParser.NextToken;
Result:=AParser.Token=preprocname;
end;
end;
{ TCPrepDefine }
function TCPrepDefine.DoParse(AParser: TTextParser): Boolean;
var
tt : TTokenType;
prs : AnsiString;
SpaceChars : TCharSet;
SymChars : TCharSet;
begin
AParser.FindNextToken(_name, tt);
Result := tt = tt_Ident;
if not Result then Exit;
if (AParser.Index<=length(AParser.Buf)) and (AParser.Buf[AParser.Index]='(') then begin
AParser.NextToken; // skipping "("
AParser.NextToken; // the first ident
Params:=TStringList.Create;
while AParser.Token<>')' do begin
if AParser.TokenType=tt_Ident then begin
Params.Add(AParser.Token);
AParser.NextToken;
end;
if AParser.Token=',' then AParser.NextToken;
end;
end;
SpaceChars := AParser.TokenTable.SpaceChars;
SymChars := AParser.TokenTable.Symbols;
with AParser.TokenTable do SpaceChars := SpaceChars - [#10,#13];
with AParser.TokenTable do Symbols := [#10, #13];
try
AParser.FindNextToken(prs, tt);
while (prs <> '') and (not (prs[1] in [#10, #13])) do begin
SubsText := SubsText + ' ' + prs;
AParser.FindNextToken(prs, tt);
end;
RemoveMacroSlash(SubsText);
if prs <> '' then AParser.Index := AParser.TokenPos;
finally
ScanWhile(AParser.Buf, AParser.Index, [#10,#13]);
AParser.TokenTable.SpaceChars := SpaceChars;
AParser.TokenTable.Symbols := SymChars;
end;
end;
destructor TCPrepDefine.Destroy;
begin
Params.Free;
inherited Destroy;
end;
{ TCPrepInclude }
function TCPrepInclude.DoParse(AParser: TTextParser): Boolean;
var
s : AnsiString;
tt : TTokenType;
exp : AnsiString;
chars : TCharSet;
begin
chars := AParser.TokenTable.Symbols;
try
AParser.TokenTable.Symbols := AParser.TokenTable.Symbols + ['"'];
AParser.FindNextToken(s, tt);
Result := (s = '"') or (s = '<');
if not Result then Exit;
if s = '"' then exp := '"'
else if s = '<' then exp := '>';
repeat
AParser.FindNextToken(s, tt);
if (s = '/') or (s = '\') or (tt = tt_Ident) then
Included := Included + s;
until (tt =tt_Symbol) and ((s <> '\') or (s <> '/'));
Result := s = exp;
SkipLine(AParser.buf, AParser.Index);
finally
AParser.TokenTable.Symbols := chars ;
end;
end;
destructor TCPrepInclude.Destroy;
begin
Params.Free;
inherited Destroy;
end;
{ TCPrepElse }
function TCPrepElse.DoParse(AParser: TTextParser): Boolean;
begin
SkipLine(AParser.buf, AParser.Index);
Result:=True;
end;
{ TCPrepEndif }
function TCPrepEndif.DoParse(AParser: TTextParser): Boolean;
begin
SkipLine(AParser.buf, AParser.Index);
Result:=True;
end;
{ TCPrepIf }
function TCPrepIf.DoParse(AParser: TTextParser): Boolean;
begin
_Cond := SkipLine(AParser.buf, AParser.Index);
Result:=True;
end;
{ TCPrepPragma }
function TCPrepPragma.DoParse(AParser: TTextParser): Boolean;
begin
_Text := SkipLine(AParser.buf, AParser.Index);
Result:=True;
end;
{ TCMacroHandler }
procedure TCMacroHandler.AddSimpleMacro(const MacroStr,
ReplaceStr: AnsiString);
begin
AddPAramMacro(MacroStr, ReplaceStr, nil);
end;
procedure TCMacroHandler.AddParamMacro(const MacroStr,ReplaceStr:AnsiString;
Params:TStrings);
var
cm : TCMacroStruct;
i : Integer;
begin
cm := TCMacroStruct.Create;
cm.MacroName := MacroStr;
cm.ReplaceText := ReplaceStr;
if Assigned(Params) then cm.MacroParams.Assign(Params);
i := MacrosNames.IndexOf(MacroStr);
if i >= 0 then begin
MacrosNames.Objects[i].Free;
MacrosNames.Delete(i);
end;
MacrosNames.AddObject(MacroStr, cm);
end;
procedure TCMacroHandler.Clear;
var
i : Integer;
begin
for i := 0 to MacrosNames.Count - 1 do MacrosNames.Objects[i].Free;
MacrosNames.Clear;
end;
constructor TCMacroHandler.Create;
begin
MacrosNames := TStringList.Create;
end;
destructor TCMacroHandler.Destroy;
begin
Clear;
MacrosNames.Free;
inherited;
end;
function TCMacroHandler.isMacroDefined(const Macro: AnsisTring): Boolean;
begin
Result := MacrosNames.IndexOf(Macro) >= 0;
end;
function MakeMacroText(const ParamNames, RepValues: TStrings; const SourceText: AnsiString): AnsiString;
var
p : TTextParser;
i : Integer;
begin
if SourceText='' then Result:='';
p:=CreateCParser(SourceText, False);
Result:='';
try
i:=1;
while p.NextToken do begin
if (p.TokenType=tt_Ident) and (ParamNames.IndexOf(p.Token)>=0) then begin
Result:=Result+Copy(p.Buf, i, p.TokenPos-i)+' ' + RepValues.Values[p.Token]+' ';
i:=p.Index;
end;
end;
if i<length(p.Buf) then
Result:=Result+Copy(p.Buf, i, p.TokenPos-i);
finally
p.Free;
end;
end;
function TCMacroHandler.ParseMacro(const Parser: TTextParser; var MacroStr,
ReplaceStr: AnsiString): Boolean;
var
s, x : AnsiString;
name : AnsiString;
tt : TTokenType;
i : Integer;
idx : Integer;
//j : Integer;
cm : TCMacroStruct;
RVal : TStringList;
begin
Parser.FindNextToken(s, tt);
i := MacrosNames.IndexOf(s);
Result := (i >= 0);
if not Result then begin
Parser.Index := Parser.TokenPos;
Exit;
end;
name:=s;
idx:=Parser.TokenPos;
cm := TCMacroStruct(MacrosNames.Objects[i]);
if Assigned(cm.MacroParams) and (cm.MacroParams.Count > 0) then begin
//j := Parser.TokenPos;
Parser.NextToken;
Result:=Parser.Token='(';
if not Result then begin
Result := False;
Parser.SetError('error while parsing macros usage');
Exit;
end;
RVal := TStringList.Create;
try
i := 0;
while Parser.Token<>')' do begin
ParseCMacroParam(Parser, x);
Result:=i<cm.MacroParams.Count;
if not Result then begin
Parser.SetError('too many params for the Macro: '+ name);
Exit;
end;
RVal.Values [ cm.MacroParams[i]]:=x;
Parser.NextToken;
if Parser.Token=',' then Parser.NextToken;
inc(i);
end;
if i<cm.MacroParams.Count then begin
Parser.SetError('not enough params for the Macro: '+ name);
Exit;
end;
MacroStr:=Copy(Parser.Buf, idx, Parser.Index-idx);
ReplaceStr:=MakeMacroText(cm.MacroParams, RVal, cm.ReplaceText);
finally
RVal.Free;
end;
end else begin
MacroStr := cm.MacroName;
ReplaceStr := cm.ReplaceText;
end;
end;
{ TCMacroStruct }
constructor TCMacroStruct.Create;
begin
MacroParams := TStringList.Create;
end;
destructor TCMacroStruct.Destroy;
begin
MacroParams.Free;
inherited;
end;
{ TObjCClassProperty }
function ParseGetterSetterName(AParser: TTextParser): AnsiString;
var
tt: TTokenType;
s : string;
begin
Result := '';
AParser.FindNextToken(s, tt);
if (tt <> tt_Symbol) and (s <> '=') then Exit;
AParser.FindNextToken(Result, tt);
end;
function isSomeSpecifier(const s: AnsiString): Boolean;
begin
Result:=length(s)>0;
if Result then
case s[1] of
'a': Result:=s='auto';
'c': Result:=s='const';
'e': Result:=s='extern';
'r': Result:=s='register';
's': Result:=s='static';
'i': Result:=s='inline';
'o': Result:=s='overload';
'v': Result:=(s='volitile') or (s='virtual');
else
Result:=False;
end;
end;
function isCallConv(const s: AnsiString): AnsiString;
var
i : Integer;
c : AnsiString;
begin
Result:='';
if s='' then Exit;
c:=s;
for i:=1 to length(c) do
if c[i]<>'_' then begin
if i>1 then c:=Copy(c, i, length(c));
Break;
end;
case c[1] of
'c': if (c='cdecl') or (c='clrcall') then Result:=c;
'f': if c='fastcall' then Result:=c;
's': if c='stdcall' then Result:=c;
't': if c='thiscall' then Result:=c;
'p': if c='pascal' then Result:=c;
'r': if c='register' then Result:=c;
end;
end;
procedure ParseSepcifiers(AParser: TTextParser; st: TStrings);
begin
while isSomeSpecifier(AParser.Token) do begin
st.Add(AParser.Token);
AParser.NextToken;
end;
end;
function ParseNextCEntity(AParser: TTextParser): TEntity;
var
s : AnsiString;
tp : TEntity;
nm : TNamePart;
v : TVarFuncEntity;
begin
Result := nil;
s:=AParser.Token;
if s='' then Exit;
if s = 'typedef' then begin
Result:=ParseTypeDef(AParser);
end else begin
v:=TVarFuncEntity.Create(AParser.TokenPos);
ParseNames(AParser, tp, v.Names, [';']);
// declarations like:
// fn (int i);
// are parsed wrongly, because name of the function "fn" is consumed by typedef
// while it's named of the function, and the returning type is unspecified.
// the name of function must be added to the name operations tree, and type should be set to nil
nm:=v.FirstName;
if Assigned(tp) and (tp is TSimpleType) and Assigned(nm) and (nm.Kind=nk_Func) and not Assigned(nm.child) then begin
nm.child:=TNamePart.Create(nk_Ident);
nm.child.Id:=TSimpleType(tp).Name; // making an untyped function
tp.Free;
tp:=nil;
end;
TVarFuncEntity(v).RetType:=tp;
if (v.Names.Count=0) and Assigned(TVarFuncEntity(v).RetType) then begin
Result:=TVarFuncEntity(v).RetType;
TVarFuncEntity(v).RetType:=nil;
v.Free;
end else
Result:=v;
end;
if AParser.Token<>';' then begin
Result.Free;
Result:=nil;
ErrorExpect(AParser,';');
end;
end;
function ParseDefPreproc(AParser: TTextParser): TEntity;
begin
Result:=nil;
end;
procedure ErrorExpect(Parser:TTextParser;const Expect:AnsiString);
begin
Parser.SetError('expected: "'+ Expect + '" but "'+Parser.Token+'" found');
end;
function ConsumeToken(Parser:TTextParser;const Token:AnsiString):Boolean;
begin
Result:=Parser.Token=Token;
if Result then Parser.NextToken
else Parser.SetError('Token expected: '+Token);
end;
function ConsumeIdentifier(Parser: TTextParser; var Id: AnsiString): Boolean;
begin
Result:=Parser.TokenType=tt_Ident;
if Result then begin
id:=Parser.Token;
Parser.NextToken;
end else
Parser.SetError('Identifier expected');
end;
function ParseCType(Parser: TTextParser): TEntity;
var
simple : TSimpleType;
issig : Boolean;
islong : Boolean;
nm : AnsiString;
begin
Result:=nil;
if (Parser.Token='struct') then
Result:=ParseStruct(Parser)
else if (Parser.Token='union') then
Result:=ParseUnion(Parser)
else if (Parser.Token='enum') then
Result:=ParseEnum(Parser)
else begin
if Parser.TokenType<>tt_Ident then Exit;
nm:='';
simple:=TSimpleType.Create(Parser.TokenPos);
issig:=(Parser.Token='unsigned') or (simple.Name='signed');
if issig then begin
nm:=nm+Parser.Token+' ';
Parser.NextToken;
end;
islong:=Parser.Token='long';
if islong then begin
nm:=nm+Parser.Token+' ';
Parser.NextToken;
end;
if (Parser.Token='long') then begin
nm:=nm+Parser.Token+' ';
Parser.NextToken;
end;
if (Parser.Token='short') then begin
nm:=nm+Parser.Token+' ';
Parser.NextToken;
end;
if (Parser.Token='char') then begin
nm:=nm+Parser.Token+' ';
Parser.NextToken
end;
if (Parser.Token='int') then begin
nm:=nm+Parser.Token+' ';
Parser.NextToken
end;
if (Parser.Token='double') then begin
nm:=nm+Parser.Token+' ';
Parser.NextToken;
end;
if nm<>'' then
simple.name:=Copy(nm, 1, length(nm)-1)
else begin
simple.name:=Parser.Token;
Parser.NextToken;
end;
Result:=simple;
end;
end;
function isEndOfExpr(const t: AnsiString; CommaIsEnd: Boolean): Boolean;
begin
Result:=(t=']') or (t=';') or (t=')') or (CommaIsEnd and (t=',')) or (t='}');
end;
function ParseCExpr(Parser: TTextParser; CommaIsEnd: Boolean=False): TExpression;
var
x : TExpression;
lvl : Integer;
begin
if isEndOfExpr(Parser.Token, CommaIsEnd) then
Result:=nil
else begin
lvl:=0;
x := TExpression.Create(Parser.Index);
repeat
if (Parser.Token='(') or (Parser.Token='[') then
inc(lvl)
else begin
if (lvl=0) and isEndOfExpr(Parser.Token, CommaIsEnd) then
Break
else if (Parser.Token=')') or (Parser.Token=']') then
dec(lvl)
end;
x.PushToken(Parser.Token, Parser.TokenType);
until not Parser.NextToken;
Result:=x;
end;
end;
{ TExpression }
procedure TExpression.PushToken(const AToken:AnsiString; ATokenType: TTokenType);
begin
if Count=length(Tokens) then begin
if Count=0 then SetLength(Tokens, 2)
else SetLength(Tokens, Count*2);
end;
Tokens[Count].Token:=AToken;
Tokens[Count].TokenType:=ATokenType;
inc(Count);
end;
procedure ParseFuncParams(Parser: TTextParser; FuncName: TNamePart);
var
prmtype : TEntity;
prmname : TNamePart;
begin
Parser.NextToken;
while Parser.Token<>')' do begin
if ParseName(Parser, prmtype, prmname, [',',')']) then begin
FuncName.AddParam(prmtype, prmname)
end else
Exit; // failure
if Parser.Token<>')' then begin
if Parser.Token=',' then
Parser.NextToken
else begin
ErrorExpect(Parser,')');
Break;
end;
end;
end;
Parser.NextToken;
end;
function ParseCNamePart(Parser: TTextParser): TNamePart;
var
prefix : TNamePart;
id : TNamePart;
postfix : TNamePart;
// todo: store const them as part of the name
begin
if Parser.Token='const' then Parser.NextToken; // skip const qualifier
if Parser.Token='*' then begin
prefix:=TNamePart.Create(nk_Ref);
while Parser.Token='*' do begin
inc(prefix.refcount);
Parser.NextToken;
if Parser.Token='const' then Parser.NextToken; // skip const qualifier
end;
end else if (Parser.Token='^') then begin
prefix:=TNamePart.Create(nk_Block);
Parser.NextToken;
end else
prefix:=nil;
if Parser.Token='(' then begin
Parser.NextToken;
id:=ParseNamePart(Parser);
ConsumeToken(Parser, ')');
end else if (Parser.TokenType=tt_Ident) then begin
id:=TNamePart.Create(nk_Ident);
id.id:=Parser.Token;
Parser.NextToken;
end else
id:=nil;
postfix:=nil;
if Parser.Token='[' then begin
while Parser.Token='[' do begin
if Assigned(postfix) then begin
postfix.child:=TNamePart.Create(nk_Array);
postfix:=postfix.child
end else
postfix:=TNamePart.Create(nk_Array);
Parser.NextToken;
postfix.AddArrayExpr(ParseCExpr(Parser));
if not ConsumeToken(Parser, ']') then Break;
end;
end else if Parser.Token='(' then begin
postfix:=TNamePart.Create(nk_Func);
ParseFuncParams(Parser, postfix);
end;
Result:=id;
if Assigned(postfix) then begin
postfix.child:=Result;
Result.owner:=postfix;
Result:=postfix;
end;
if Assigned(prefix) then begin
if Assigned(Result) and (Result.Kind=nk_Ref) then begin
inc(Result.RefCount, prefix.RefCount);
prefix.Free;
end else begin
prefix.child:=Result;
if Assigned(Result) then Result.owner:=prefix;
Result:=prefix;
end;
end;
end;
function isEndOfName(APArser: TTextParser; const EndChars: TCharSet): Boolean;
begin
Result:=(AParser.TokenType=tt_Symbol) and (AParser.Token[1] in EndChars);
end;
function ParseNames(Parser: TTextParser; var NameType: TEntity; Names: TList; const EndChars: TCharSet; AllowMultipleNames: Boolean): Boolean;
var
Name : TNamePart;
done : Boolean;
specs : TStringList;
s : AnsiString;
begin
specs:=TStringList.Create;
ParseSepcifiers(Parser, specs);
NameType:=ParseCType(Parser);
s:=isCallConv(Parser.Token);
if s<>'' then begin
specs.Add(s);
Parser.NextToken;
end;
Result:=Assigned(NameType);
if Result then NameType.Specifiers.Assign(specs);
specs.Free;
if not Result then Exit;
try
Result:=False;
repeat
Name:=ParseNamePart(Parser);
if Assigned(Name) then Names.Add(Name);
if not AllowMultipleNames then begin
Result:=True;
Exit;
end;
done:=isEndOfName(Parser, EndChars);
if not done then begin
if Parser.Token <> ',' then begin
ErrorExpect(Parser, ';');
Exit;
end;
Parser.NextToken;
end;
until done;
Result:=True;
finally
end;
end;
function ParseName(Parser: TTextParser; var NameType: TEntity; var name: TNamePart; const EndChars: TCharSet): Boolean;
var
nm : TList;
begin
nm:=TList.Create;
try
name:=nil;
NameType:=nil;
Result:=ParseNames(Parser, NameType, nm, EndChars, False);
if Result and (nm.Count>0) then name:=TNamePart(nm[0]);
finally
nm.Free;
end;
end;
{ TNamePart }
constructor TNamePart.Create(AKind:TNameKind);
begin
inherited Create;
Kind:=AKind;
end;
destructor TNamePart.Destroy;
var
i : Integer;
begin
if Assigned(fChild) then begin
fChild.owner:=nil;
fChild.Free;
end;
if Assigned(fOwner) then fOwner.fChild:=nil;
for i:=0 to length(arrayexp)-1 do arrayexp[i].Free;
for i:=0 to length(params)-1 do begin
params[i].prmtype.Free;
params[i].name.Free;
end;
inherited Destroy;
end;
procedure TNamePart.AddParam(prmtype:TEntity;prmname:TNamePart);
var
i : Integer;
begin
i:=length(Params);
SetLength(Params, i+1);
Params[i].prmtype:=prmtype;
Params[i].name:=prmname;
end;
procedure TNamePart.AddArrayExpr(expr:TExpression);
var
i : Integer;
begin
i:=length(arrayexp);
SetLength(arrayexp, i+1);
arrayexp[i]:=expr;
end;
{ TVarFuncEntity }
constructor TVarFuncEntity.Create(AOffset: Integer);
begin
inherited Create(AOffset);
Names:=TList.Create;
end;
destructor TVarFuncEntity.Destroy;
var
i : Integer;
begin
RetType.Free;
for i:=0 to Names.Count-1 do TObject(Names[i]).Free;
Names.Free;
inherited Destroy;
end;
function TVarFuncEntity.FirstName:TNamePart;
begin
if Names.Count>0 then Result:=TNamePart(Names[0]) else Result:=nil;
end;
{ TStructType }
destructor TStructType.Destroy;
var
i : Integer;
begin
for i:=0 to length(fields)-1 do begin
fields[i].v.Free;
fields[i].bits.Free;
end;
inherited Destroy;
end;
function TStructType.AddField(ev:TVarFuncEntity):Integer;
var
i : Integer;
begin
i:=length(fields);
SetLength(fields, i+1);
fields[i].v:=ev;
Result:=i;
end;
function ParseStruct(AParser: TTextParser): TStructType;
var
i : Integer;
st : TStructType;
v : TVarFuncEntity;
begin
Result:=nil;
if AParser.Token<>'struct' then Exit;
st:=TStructType.Create(AParser.TokenPos);
try
AParser.NextToken;
Result:=st;
if AParser.TokenType=tt_Ident then begin
Result.Name:=AParser.Token;
AParser.NextToken;
end;
if AParser.Token='{' then begin
AParser.NextToken;
repeat
v:=TVarFuncEntity.Create(AParser.TokenPos);
if not ParseNames(AParser, v.RetType, v.Names,[';',':']) then begin
ErrorExpect(AParser, 'type name');
v.Free;
Exit;
end;
i:=st.AddField(v);
if AParser.Token=':' then begin
AParser.NextToken;
st.fields[i].isbitted:=True;
st.fields[i].bits:=ParseCExpr(AParser);
end;
if AParser.Token=';' then AParser.NextToken;
until (AParser.Token='}');
if not ConsumeToken(AParser, '}') then Exit;
end;
Result:=st;
finally
if not Assigned(Result) then st.Free;
end;
end;
function ParseUnion(AParser:TTextParser):TUnionType;
var
i : Integer;
st : TUnionType;
v : TVarFuncEntity;
begin
Result:=nil;
if AParser.Token<>'union' then Exit;
st:=TUnionType.Create(AParser.TokenPos);
AParser.NextToken;
Result:=st;
if AParser.TokenType=tt_Ident then begin
Result.Name:=AParser.Token;
AParser.NextToken;
end;
if AParser.Token<>'{' then begin
ErrorExpect(AParser, '{');
Exit;
end;
AParser.NextToken;
try
repeat
v:=TVarFuncEntity.Create(AParser.TokenPos);
if not ParseNames(AParser, v.RetType, v.Names,[';']) then begin
ErrorExpect(AParser, 'type name');
v.Free;
Exit;
end;
i:=st.AddField(v);
if AParser.Token=':' then begin
AParser.NextToken;
st.fields[i].bits:=ParseCExpr(AParser);
end;
if AParser.Token=';' then AParser.NextToken;
until (AParser.Token='}');
ConsumeToken(AParser, '}');
Result:=st;
finally
if not Assigned(Result) then st.Free;
end;
end;
function ParseTypeDef(AParser: TTextParser): TTypeDef;
var
td : TTypeDef;
begin
Result:=nil;
if AParser.Token<>'typedef' then Exit;
try
td:=TTypeDef.Create(AParser.TokenPos);
AParser.NextToken;
Result:=td;
ParseNames(AParser, td.origintype, td.names, [';'], true);
finally
if not Assigned(Result) then
td.Free;
end;
end;
function ParseEnum(AParser: TTextParser): TEnumType;
var
en : TEnumType;
nm : AnsiString;
x : TExpression;
ofs : Integer;
begin
Result:=nil;
en:=nil;
try
if AParser.Token<>'enum' then Exit;
en:=TEnumType.Create(AParser.TokenPos);
AParser.NextToken;
if AParser.TokenType=tt_Ident then begin
en.Name:=AParser.Token;
AParser.NextToken;
end;
if AParser.Token='{' then begin
AParser.NextToken;
while AParser.Token<>'}' do begin
if AParser.TokenType<>tt_Ident then begin
ErrorExpect(AParser, 'identifier');
Exit;
end;
ofs:=AParser.TokenPos;
if not ConsumeIdentifier(AParser,nm) then Exit;
if AParser.Token='=' then begin
AParser.NextToken;
x:=ParseCExpr(AParser, True);
if not Assigned(x) then Exit;
end else
x:=nil;
en.AddItem(nm, x, ofs);
if AParser.Token=',' then AParser.NextToken;
end;
if not ConsumeToken(AParser, '}') then Exit;
end;
Result:=en;
finally
if not Assigned(Result) then en.Free;
end;
end;
{ TUnionType }
function TUnionType.AddField(ev:TVarFuncEntity):Integer;
var
i : Integer;
begin
i:=length(fields);
SetLength(fields, i+1);
fields[i].v:=ev;
Result:=i;
end;
destructor TUnionType.Destroy;
var
i : Integer;
begin
for i:=0 to length(fields)-1 do begin
fields[i].v.Free;
fields[i].bits.Free;
end;
inherited Destroy;
end;
{ TEnumType }
function TEnumType.AddItem(const name:AnsiString;x:TExpression; Offset: Integer): Integer;
var
i : Integer;
begin
i:=length(items);
SetLength(items, i+1);
items[i].Name := name;
items[i].Value := x;
items[i].Offset:=Offset;
Result:=i;
end;
{ TTypeDef }
constructor TTypeDef.Create(AOffset:Integer);
begin
inherited Create(AOffset);
names:=TList.Create;
end;
destructor TTypeDef.Destroy;
var
i : Integer;
begin
origintype.Free;
for i:=0 to names.Count-1 do TObject(names[i]).Free;
names.Free;
inherited Destroy;
end;
initialization
ParseNextEntity:=@ParseNextCEntity;
ParseNamePart:=@ParseCNamePart;
ParsePreproc:=@ParseDefPreproc;
end.