mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-05 20:32:40 +02:00
1629 lines
44 KiB
ObjectPascal
1629 lines
44 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source 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 code 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. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
A simple C parser.
|
|
}
|
|
unit CCodeParserTool;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{$I codetools.inc}
|
|
|
|
{off $DEFINE VerboseCCodeParser}
|
|
|
|
uses
|
|
{$IFDEF MEM_CHECK}
|
|
MemCheck,
|
|
{$ENDIF}
|
|
Classes, SysUtils, FileProcs, CodeToolsStructs, BasicCodeTools,
|
|
KeywordFuncLists, LinkScanner, CodeAtom, CodeCache, AVL_Tree,
|
|
CodeToolsStrConsts, CodeTree, NonPascalCodeTools;
|
|
|
|
type
|
|
TCCodeNodeDesc = word;
|
|
|
|
const
|
|
// descriptors
|
|
ccnBase = 1000;
|
|
ccnNone = 0+ccnBase;
|
|
|
|
ccnRoot = 1+ccnBase;
|
|
ccnDirective = 2+ccnBase;// e.g. "#define a" ,can be multiple lines, without line end
|
|
ccnExtern = 3+ccnBase;// e.g. extern "C" {}
|
|
ccnEnumBlock = 4+ccnBase;// e.g. enum {};
|
|
ccnEnumID = 5+ccnBase;// e.g. name = value;
|
|
ccnConstant = 6+ccnBase;// e.g. 1
|
|
ccnTypedef = 7+ccnBase;// e.g. typedef int TInt;
|
|
ccnStruct = 8+ccnBase;// e.g. struct{}
|
|
ccnUnion = 9+ccnBase;// e.g. union{}
|
|
ccnVariable = 10+ccnBase;// e.g. int i
|
|
ccnFunction = 11+ccnBase;// e.g. int i()
|
|
ccnName = 12+ccnBase;// e.g. i
|
|
ccnFuncParamList = 13+ccnBase;// e.g. ()
|
|
ccnFuncParameter = 14+ccnBase;// e.g. ()
|
|
ccnStatementBlock = 15+ccnBase;// e.g. {}
|
|
|
|
type
|
|
TCCodeParserTool = class;
|
|
|
|
{ ECCodeParserException }
|
|
|
|
ECCodeParserException = class(Exception)
|
|
public
|
|
Sender: TCCodeParserTool;
|
|
constructor Create(ASender: TCCodeParserTool; const AMessage: string);
|
|
end;
|
|
|
|
{ TCCodeParserTool }
|
|
|
|
TCCodeParserTool = class
|
|
private
|
|
FChangeStep: integer;
|
|
FDefaultTokenList: TKeyWordFunctionList;
|
|
|
|
function OtherToken: boolean;
|
|
function DirectiveToken: boolean;
|
|
function EnumToken: boolean;
|
|
function ExternToken: boolean;
|
|
function CurlyBracketCloseToken: boolean;
|
|
function TypedefToken: boolean;
|
|
function StructToken: boolean;
|
|
procedure InitKeyWordList;
|
|
|
|
procedure InitParser;
|
|
procedure CreateChildNode(Desc: TCCodeNodeDesc);
|
|
procedure EndChildNode;
|
|
procedure CloseNodes;
|
|
|
|
procedure ReadVariable;
|
|
procedure ReadParameterList;
|
|
procedure ReadEnum;
|
|
procedure ReadStruct;
|
|
procedure ReadUnion;
|
|
procedure ReadConstant;
|
|
|
|
procedure RaiseException(const AMessage: string; ReportPos: integer = 0);
|
|
procedure RaiseExpectedButAtomFound(const AToken: string; ReportPos: integer = 0);
|
|
public
|
|
Code: TCodeBuffer;
|
|
Src: string;
|
|
SrcLen: integer;
|
|
Tree: TCodeTree;
|
|
CurNode: TCodeTreeNode;
|
|
SrcPos: Integer;
|
|
AtomStart: integer;
|
|
ParseChangeStep: integer;// = Code.ChangeStep at the time of last Parse
|
|
|
|
VisibleEditorLines: integer;
|
|
JumpCentered: boolean;
|
|
CursorBeyondEOL: boolean;
|
|
|
|
LastSrcPos: integer;
|
|
LastAtomStart: integer;
|
|
|
|
LastErrorMsg: string;
|
|
LastErrorPos: integer; // the position where the code does no make sense
|
|
LastErrorReportPos: integer; // if the position that gives a human a clue what went wrong
|
|
// normally LastErrorReportPos=LastErrorPos
|
|
// but if a closing bracket is missing LastErrorReportPos points
|
|
// to ( and ErrorPos to next atom
|
|
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
|
|
procedure Parse;
|
|
procedure Parse(aCode: TCodeBuffer);
|
|
function UpdateNeeded: boolean;
|
|
|
|
function FindDeepestNodeAtPos(P: integer;
|
|
ExceptionOnNotFound: boolean): TCodeTreeNode; inline;
|
|
function FindDeepestNodeAtPos(StartNode: TCodeTreeNode; P: integer;
|
|
ExceptionOnNotFound: boolean): TCodeTreeNode;
|
|
function CaretToCleanPos(Caret: TCodeXYPosition;
|
|
out CleanPos: integer): integer; // 0=valid CleanPos
|
|
//-1=CursorPos was skipped, CleanPos between two links
|
|
// 1=CursorPos beyond scanned code
|
|
//-2=X,Y beyond source
|
|
function CleanPosToCodePos(CleanPos: integer;
|
|
out CodePos:TCodePosition): boolean; // true=ok, false=invalid CleanPos
|
|
function CleanPosToCaret(CleanPos: integer;
|
|
out Caret:TCodeXYPosition): boolean; // true=ok, false=invalid CleanPos
|
|
function CleanPosToCaretAndTopLine(CleanPos: integer;
|
|
out Caret:TCodeXYPosition; out NewTopLine: integer): boolean; // true=ok, false=invalid CleanPos
|
|
function CleanPosToStr(CleanPos: integer): string;
|
|
function MainFilename: string;
|
|
|
|
procedure MoveCursorToPos(p: integer);
|
|
procedure MoveCursorToNode(Node: TCodeTreeNode);
|
|
procedure ReadNextAtom;
|
|
procedure ReadNextAtomSkipDirectives;
|
|
procedure UndoReadNextAtom;
|
|
function ReadTilBracketClose(ExceptionOnNotFound: boolean): boolean;
|
|
function AtomIs(const s: shortstring): boolean;
|
|
function AtomIsChar(const c: char): boolean;
|
|
function UpAtomIs(const s: shortstring): boolean;
|
|
function AtomIsIdentifier: boolean;
|
|
function AtomIsStringConstant: boolean;
|
|
function GetAtom: string;
|
|
function LastAtomIs(const s: shortstring): boolean;
|
|
function GetLastAtom: string;
|
|
function ExtractCode(StartPos, EndPos: integer;
|
|
WithDirectives: boolean = false): string;// extract code without comments
|
|
|
|
function GetFirstNameNode(Node: TCodeTreeNode): TCodeTreeNode;
|
|
function ExtractVariableName(VarNode: TCodeTreeNode): string;
|
|
function ExtractVariableType(VarNode: TCodeTreeNode;
|
|
WithDirectives: boolean = false): string;
|
|
function ExtractFunctionName(FuncNode: TCodeTreeNode): string;
|
|
function ExtractFunctionType(FuncNode: TCodeTreeNode;
|
|
WithDirectives: boolean = false): string;
|
|
function ExtractFunctionResultType(FuncNode: TCodeTreeNode;
|
|
WithDirectives: boolean = false): string;
|
|
function IsPointerToFunction(FuncNode: TCodeTreeNode): boolean;
|
|
function ExtractParameterName(ParamNode: TCodeTreeNode): string;
|
|
function ExtractParameterType(ParamNode: TCodeTreeNode;
|
|
WithDirectives: boolean = false): string;
|
|
function ExtractEnumBlockName(EnumBlockNode: TCodeTreeNode): string;
|
|
function ExtractEnumIDName(EnumIDNode: TCodeTreeNode): string;
|
|
function ExtractEnumIDValue(EnumIDNode: TCodeTreeNode;
|
|
WithDirectives: boolean = false): string;
|
|
function ExtractStructName(StructNode: TCodeTreeNode): string;
|
|
function ExtractTypedefName(TypedefNode: TCodeTreeNode): string;
|
|
|
|
procedure Replace(FromPos, ToPos: integer; const NewSrc: string);
|
|
|
|
procedure IncreaseChangeStep;
|
|
procedure WriteDebugReport;
|
|
procedure CheckNodeTool(Node: TCodeTreeNode);
|
|
|
|
property ChangeStep: integer read FChangeStep;
|
|
end;
|
|
|
|
function CCNodeDescAsString(Desc: TCCodeNodeDesc): string;
|
|
procedure InitCCodeKeyWordLists;
|
|
|
|
var
|
|
IsCCodeFunctionModifier: TKeyWordFunctionList = nil;
|
|
IsCCodeCustomOperator: TKeyWordFunctionList = nil;
|
|
|
|
implementation
|
|
|
|
var
|
|
KeyWordLists: TFPList;
|
|
|
|
function CCNodeDescAsString(Desc: TCCodeNodeDesc): string;
|
|
begin
|
|
case Desc of
|
|
ccnNone : Result:='None';
|
|
ccnRoot : Result:='Root';
|
|
ccnDirective : Result:='Directive';
|
|
ccnExtern : Result:='extern-block';
|
|
ccnEnumBlock : Result:='enum-block';
|
|
ccnEnumID : Result:='enum-ID';
|
|
ccnConstant : Result:='constant';
|
|
ccnTypedef : Result:='typedef';
|
|
ccnStruct : Result:='struct';
|
|
ccnUnion : Result:='union';
|
|
ccnVariable : Result:='variable';
|
|
ccnFunction : Result:='function';
|
|
ccnName : Result:='name';
|
|
ccnFuncParamList : Result:='function-param-list';
|
|
ccnFuncParameter : Result:='function-parameter';
|
|
ccnStatementBlock: Result:='statement-block';
|
|
else Result:='?('+IntToStr(Desc)+')';
|
|
end;
|
|
end;
|
|
|
|
procedure InitCCodeKeyWordLists;
|
|
begin
|
|
if KeyWordLists<>nil then exit;
|
|
KeyWordLists:=TFPList.Create;
|
|
|
|
IsCCodeFunctionModifier:=TKeyWordFunctionList.Create;
|
|
KeyWordLists.Add(IsCCodeFunctionModifier);
|
|
with IsCCodeFunctionModifier do begin
|
|
Add('static' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('inline' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
end;
|
|
|
|
IsCCodeCustomOperator:=TKeyWordFunctionList.Create;
|
|
KeyWordLists.Add(IsCCodeCustomOperator);
|
|
with IsCCodeCustomOperator do begin
|
|
Add('+' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('-' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('*' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('/' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('|' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('&' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('=' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('++' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('--' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('+=' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('-=' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('*=' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('/=' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('&=' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('|=' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('==' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
Add('!=' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
|
end;
|
|
end;
|
|
|
|
{ ECCodeParserException }
|
|
|
|
constructor ECCodeParserException.Create(ASender: TCCodeParserTool;
|
|
const AMessage: string);
|
|
begin
|
|
inherited Create(AMessage);
|
|
Sender:=ASender;
|
|
end;
|
|
|
|
{ TCCodeParserTool }
|
|
|
|
function TCCodeParserTool.OtherToken: boolean;
|
|
begin
|
|
Result:=true;
|
|
if AtomIsChar(';') then
|
|
// ignore
|
|
else if AtomIsIdentifier then begin
|
|
ReadVariable;
|
|
end else
|
|
RaiseException('unexpected token '+GetAtom);
|
|
end;
|
|
|
|
function TCCodeParserTool.DirectiveToken: boolean;
|
|
begin
|
|
Result:=true;
|
|
CreateChildNode(ccnDirective);
|
|
// read til end of line
|
|
ReadTilCLineEnd(Src,SrcPos);
|
|
AtomStart:=SrcPos;
|
|
EndChildNode;
|
|
end;
|
|
|
|
function TCCodeParserTool.EnumToken: boolean;
|
|
begin
|
|
Result:=true;
|
|
ReadEnum;
|
|
// read semicolon
|
|
ReadNextAtom;
|
|
if not AtomIsChar(';') then
|
|
RaiseExpectedButAtomFound(';');
|
|
end;
|
|
|
|
function TCCodeParserTool.ExternToken: boolean;
|
|
begin
|
|
Result:=true;
|
|
CreateChildNode(ccnExtern);
|
|
ReadNextAtom;
|
|
if not AtomIsStringConstant then
|
|
RaiseExpectedButAtomFound('string constant');
|
|
ReadNextAtom;
|
|
if not AtomIsChar('{') then
|
|
RaiseExpectedButAtomFound('{');
|
|
end;
|
|
|
|
function TCCodeParserTool.CurlyBracketCloseToken: boolean;
|
|
// examples:
|
|
// end of 'extern "C" {'
|
|
begin
|
|
Result:=true;
|
|
if CurNode.Desc=ccnExtern then
|
|
EndChildNode
|
|
else
|
|
RaiseException('} without {');
|
|
end;
|
|
|
|
procedure TCCodeParserTool.ReadEnum;
|
|
(* For example:
|
|
enum {
|
|
TEST_ENUM1 = 1, /* Enum starts at 1 */
|
|
TEST_ENUM2,
|
|
TEST_ENUM3
|
|
};
|
|
enum e1{dark, light};
|
|
|
|
*)
|
|
begin
|
|
CreateChildNode(ccnEnumBlock);
|
|
ReadNextAtom;
|
|
// read optional name
|
|
if AtomIsIdentifier then begin
|
|
CreateChildNode(ccnName);
|
|
EndChildNode;
|
|
ReadNextAtom;
|
|
end;
|
|
if not AtomIsChar('{') then
|
|
RaiseExpectedButAtomFound('{');
|
|
// read enums. Examples
|
|
// name,
|
|
// name = constant,
|
|
ReadNextAtom;
|
|
repeat
|
|
if AtomIsIdentifier then begin
|
|
// read enum
|
|
CreateChildNode(ccnEnumID);
|
|
CurNode.EndPos:=SrcPos;
|
|
ReadNextAtom;
|
|
if AtomIsChar('=') then begin
|
|
// read value
|
|
ReadNextAtom;
|
|
ReadConstant;
|
|
CurNode.EndPos:=SrcPos;
|
|
ReadNextAtom;
|
|
end;
|
|
EndChildNode;
|
|
end;
|
|
if AtomIsChar(',') then begin
|
|
// next enum
|
|
ReadNextAtom;
|
|
if not AtomIsIdentifier then
|
|
RaiseExpectedButAtomFound('identifier');
|
|
end else if AtomIsChar('}') then begin
|
|
break;
|
|
end else
|
|
RaiseExpectedButAtomFound('}');
|
|
until false;
|
|
EndChildNode;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.ReadStruct;
|
|
(* Examples:
|
|
|
|
As typedef:
|
|
typedef struct {
|
|
uint8_t b[6]; // implicit type
|
|
} __attribute__((packed)) bdaddr_t;
|
|
|
|
typedef struct _sdp_list sdp_list_t;
|
|
|
|
As implicit typedef:
|
|
struct hidp_connadd_req {
|
|
int ctrl_sock;
|
|
}
|
|
|
|
As variable:
|
|
struct hidp_conninfo *ci;
|
|
|
|
*)
|
|
begin
|
|
CreateChildNode(ccnStruct);
|
|
|
|
ReadNextAtom;
|
|
if CurNode.Parent.Desc<>ccnTypedef then begin
|
|
// read variable name
|
|
if not AtomIsIdentifier then
|
|
RaiseExpectedButAtomFound('identifier');
|
|
CreateChildNode(ccnName);
|
|
EndChildNode;
|
|
ReadNextAtom;
|
|
end;
|
|
|
|
if AtomIsChar('{') then begin
|
|
// read block {}
|
|
repeat
|
|
ReadNextAtom;
|
|
// read variables
|
|
if AtomIsIdentifier then begin
|
|
ReadVariable;
|
|
ReadNextAtom;
|
|
if AtomIsChar('}') then
|
|
break
|
|
else if AtomIsChar(';') then begin
|
|
// next identifier
|
|
end else
|
|
RaiseExpectedButAtomFound('}');
|
|
end else if AtomIsChar('}') then
|
|
break
|
|
else
|
|
RaiseExpectedButAtomFound('identifier');
|
|
until false;
|
|
// read attributes
|
|
ReadNextAtom;
|
|
if AtomIs('__attribute__') then begin
|
|
ReadNextAtom;
|
|
if not AtomIsChar('(') then
|
|
RaiseExpectedButAtomFound('(');
|
|
ReadTilBracketClose(true);
|
|
end else begin
|
|
UndoReadNextAtom;
|
|
end;
|
|
end else if AtomIsIdentifier then begin
|
|
// using another struct
|
|
end else
|
|
RaiseExpectedButAtomFound('{');
|
|
|
|
// close node
|
|
EndChildNode;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.ReadUnion;
|
|
(* Example
|
|
union {
|
|
uint16_t uuid16;
|
|
uint32_t uuid32;
|
|
uint128_t uuid128;
|
|
} value;
|
|
|
|
*)
|
|
begin
|
|
CreateChildNode(ccnUnion);
|
|
|
|
ReadNextAtom;
|
|
|
|
if AtomIsChar('{') then begin
|
|
// read block {}
|
|
repeat
|
|
ReadNextAtom;
|
|
// read variables
|
|
if AtomIsIdentifier then begin
|
|
ReadVariable;
|
|
ReadNextAtom;
|
|
if AtomIsChar('}') then
|
|
break
|
|
else if AtomIsChar(';') then begin
|
|
// next identifier
|
|
end else
|
|
RaiseExpectedButAtomFound('}');
|
|
end else if AtomIsChar('}') then
|
|
break
|
|
else
|
|
RaiseExpectedButAtomFound('identifier');
|
|
until false;
|
|
end else if AtomIsIdentifier then begin
|
|
// using another union
|
|
end else
|
|
RaiseExpectedButAtomFound('{');
|
|
|
|
// close node
|
|
EndChildNode;
|
|
end;
|
|
|
|
function TCCodeParserTool.TypedefToken: boolean;
|
|
{ examples:
|
|
typedef type name;
|
|
}
|
|
begin
|
|
Result:=true;
|
|
CreateChildNode(ccnTypedef);
|
|
// read type
|
|
ReadNextAtom;
|
|
if AtomIs('typedef') then
|
|
RaiseExpectedButAtomFound('declaration')
|
|
else if AtomIs('struct') then begin
|
|
ReadStruct;
|
|
ReadNextAtom;
|
|
if not AtomIsIdentifier then
|
|
RaiseExpectedButAtomFound('identifier');
|
|
CreateChildNode(ccnName);
|
|
EndChildNode;
|
|
end else if AtomIs('enum') then begin
|
|
ReadEnum;
|
|
ReadNextAtom;
|
|
if not AtomIsIdentifier then
|
|
RaiseExpectedButAtomFound('identifier');
|
|
CreateChildNode(ccnName);
|
|
EndChildNode;
|
|
end else if SrcPos>SrcLen then
|
|
RaiseException('missing declaration')
|
|
else
|
|
ReadVariable;
|
|
// read semicolon
|
|
ReadNextAtom;
|
|
if not AtomIsChar(';') then
|
|
RaiseExpectedButAtomFound(';');
|
|
EndChildNode;
|
|
end;
|
|
|
|
function TCCodeParserTool.StructToken: boolean;
|
|
begin
|
|
Result:=true;
|
|
ReadStruct;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.InitKeyWordList;
|
|
begin
|
|
if FDefaultTokenList=nil then begin
|
|
FDefaultTokenList:=TKeyWordFunctionList.Create;
|
|
with FDefaultTokenList do begin
|
|
Add('#',{$ifdef FPC}@{$endif}DirectiveToken);
|
|
Add('extern',{$ifdef FPC}@{$endif}ExternToken);
|
|
Add('}',{$ifdef FPC}@{$endif}CurlyBracketCloseToken);
|
|
Add('enum',{$ifdef FPC}@{$endif}EnumToken);
|
|
Add('typedef',{$ifdef FPC}@{$endif}TypedefToken);
|
|
Add('struct',{$ifdef FPC}@{$endif}StructToken);
|
|
DefaultKeyWordFunction:={$ifdef FPC}@{$endif}OtherToken;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.InitParser;
|
|
begin
|
|
ParseChangeStep:=Code.ChangeStep;
|
|
IncreaseChangeStep;
|
|
InitKeyWordList;
|
|
Src:=Code.Source;
|
|
SrcLen:=length(Src);
|
|
if Tree=nil then
|
|
Tree:=TCodeTree.Create
|
|
else
|
|
Tree.Clear;
|
|
SrcPos:=1;
|
|
AtomStart:=1;
|
|
CurNode:=nil;
|
|
CreateChildNode(ccnRoot);
|
|
end;
|
|
|
|
procedure TCCodeParserTool.CreateChildNode(Desc: TCCodeNodeDesc);
|
|
var
|
|
NewNode: TCodeTreeNode;
|
|
begin
|
|
NewNode:=NodeMemManager.NewNode;
|
|
Tree.AddNodeAsLastChild(CurNode,NewNode);
|
|
NewNode.Desc:=Desc;
|
|
CurNode:=NewNode;
|
|
CurNode.StartPos:=AtomStart;
|
|
{$IFDEF VerboseCCodeParser}
|
|
DebugLn([GetIndentStr(CurNode.GetLevel*2),'TCCodeParserTool.CreateChildNode ',CCNodeDescAsString(Desc)]);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TCCodeParserTool.EndChildNode;
|
|
begin
|
|
{$IFDEF VerboseCCodeParser}
|
|
DebugLn([GetIndentStr(CurNode.GetLevel*2),'TCCodeParserTool.EndChildNode ',CCNodeDescAsString(CurNode.Desc)]);
|
|
{$ENDIF}
|
|
if CurNode.EndPos<=0 then
|
|
CurNode.EndPos:=SrcPos;
|
|
CurNode:=CurNode.Parent;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.CloseNodes;
|
|
var
|
|
Node: TCodeTreeNode;
|
|
begin
|
|
Node:=CurNode;
|
|
while Node<>nil do begin
|
|
Node.EndPos:=AtomStart;
|
|
Node:=Node.Parent;
|
|
end;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.ReadConstant;
|
|
// ends on last atom of constant
|
|
begin
|
|
if AtomIsChar(',') or AtomIsChar(';') or AtomIsChar(')') then
|
|
RaiseExpectedButAtomFound('identifier');
|
|
CreateChildNode(ccnConstant);
|
|
repeat
|
|
if AtomIsChar('(') or AtomIsChar('[') then
|
|
ReadTilBracketClose(true);
|
|
CurNode.EndPos:=SrcPos;
|
|
ReadNextAtom;
|
|
if AtomIsChar(',') or AtomIsChar(';')
|
|
or AtomIsChar(')') or AtomIsChar(']') or AtomIsChar('}')
|
|
then
|
|
break;
|
|
until false;
|
|
UndoReadNextAtom;
|
|
EndChildNode;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.ReadVariable;
|
|
(* Read type name [specifiers]
|
|
|
|
Examples:
|
|
|
|
int i
|
|
uint8_t b[6]
|
|
uint8_t lap[MAX_IAC_LAP][3];
|
|
int y = 7;
|
|
|
|
static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
|
|
{
|
|
return memcmp(ba1, ba2, sizeof(bdaddr_t));
|
|
}
|
|
bdaddr_t *strtoba(const char *str);
|
|
*)
|
|
{
|
|
int (*fp)(char*); // pointer to function taking a char* argument; returns an int
|
|
int * f(char*); // function taking a char* argument; returns a pointer to int
|
|
|
|
complex operator+(complex, complex);
|
|
}
|
|
var
|
|
IsFunction: Boolean;
|
|
NeedEnd: Boolean;
|
|
LastIsName: Boolean;
|
|
MainNode: TCodeTreeNode;
|
|
begin
|
|
{$IFDEF VerboseCCodeParser}
|
|
DebugLn(['TCCodeParserTool.ReadVariable START ',GetAtom]);
|
|
{$ENDIF}
|
|
CreateChildNode(ccnVariable);
|
|
MainNode:=CurNode;
|
|
IsFunction:=false;
|
|
if AtomIs('struct') then begin
|
|
// for example: struct structname varname
|
|
ReadNextAtom;
|
|
end else if AtomIs('union') then begin
|
|
ReadUnion;
|
|
end else if IsCCodeFunctionModifier.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart)
|
|
then begin
|
|
// read function modifiers
|
|
while IsCCodeFunctionModifier.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart)
|
|
do begin
|
|
IsFunction:=true;
|
|
MainNode.Desc:=ccnFunction;
|
|
ReadNextAtom;
|
|
if not AtomIsIdentifier then
|
|
RaiseExpectedButAtomFound('identifier');
|
|
end;
|
|
end;
|
|
|
|
if AtomIs('const') then ReadNextAtom;
|
|
|
|
// prefixes: signed, unsigned
|
|
// prefixes and/or names long, short
|
|
|
|
// int, short int, short signed int
|
|
// char, signed char, unsigned char
|
|
// singed short, unsigned short, short
|
|
// long, long long, signed long, signed long long, unsigned long, unsigned long long
|
|
LastIsName:=false;
|
|
repeat
|
|
if AtomIs('signed') or AtomIs('unsigned') then begin
|
|
LastIsName:=false;
|
|
ReadNextAtom;
|
|
end else if AtomIs('short') or AtomIs('long') then begin
|
|
LastIsName:=true;
|
|
ReadNextAtom;
|
|
end else
|
|
break;
|
|
until false;
|
|
if LastIsName then
|
|
UndoReadNextAtom;
|
|
|
|
// read name
|
|
ReadNextAtom;
|
|
CreateChildNode(ccnName);
|
|
if AtomIs('operator') then begin
|
|
IsFunction:=true;
|
|
MainNode.Desc:=ccnFunction;
|
|
// read operator
|
|
ReadNextAtom;
|
|
CurNode.StartPos:=AtomStart;
|
|
if not IsCCodeCustomOperator.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart)
|
|
then
|
|
RaiseExpectedButAtomFound('operator');
|
|
CurNode.EndPos:=SrcPos;
|
|
end else if AtomIsChar('(') then begin
|
|
IsFunction:=true;
|
|
MainNode.Desc:=ccnFunction;
|
|
// example: int (*fp)(char*);
|
|
// pointer to function taking a char* argument; returns an int
|
|
ReadNextAtom;
|
|
while AtomIsChar('*') or AtomIs('const') do begin
|
|
// pointer or const
|
|
ReadNextAtom;
|
|
end;
|
|
{$IFDEF VerboseCCodeParser}
|
|
DebugLn(['TCCodeParserTool.ReadVariable name=',GetAtom]);
|
|
{$ENDIF}
|
|
CurNode.StartPos:=AtomStart;
|
|
if not AtomIsIdentifier then
|
|
RaiseExpectedButAtomFound('identifier');
|
|
CurNode.EndPos:=SrcPos;
|
|
ReadNextAtom;
|
|
if not AtomIsChar(')') then
|
|
RaiseExpectedButAtomFound(')');
|
|
end else begin
|
|
while AtomIsChar('*') or AtomIs('const') do begin
|
|
// pointer or const
|
|
ReadNextAtom;
|
|
end;
|
|
|
|
{$IFDEF VerboseCCodeParser}
|
|
DebugLn(['TCCodeParserTool.ReadVariable name=',GetAtom]);
|
|
{$ENDIF}
|
|
CurNode.StartPos:=AtomStart;
|
|
if not AtomIsIdentifier then
|
|
RaiseExpectedButAtomFound('identifier');
|
|
CurNode.EndPos:=SrcPos;
|
|
end;
|
|
// end of name
|
|
EndChildNode;
|
|
|
|
ReadNextAtom;
|
|
if IsFunction and (not AtomIsChar('(')) then
|
|
RaiseExpectedButAtomFound('(');
|
|
NeedEnd:=true;
|
|
if AtomIsChar('(') then begin
|
|
// this is a function => read parameter list
|
|
IsFunction:=true;
|
|
MainNode.Desc:=ccnFunction;
|
|
ReadParameterList;
|
|
ReadNextAtom;
|
|
if CurNode.Parent.Desc=ccnTypedef then begin
|
|
if AtomIsChar('{') then
|
|
RaiseException('typedef can not have a statement block');
|
|
end else begin
|
|
if AtomIsChar('{') then begin
|
|
// read statements {}
|
|
CreateChildNode(ccnStatementBlock);
|
|
ReadTilBracketClose(true);
|
|
CurNode.EndPos:=SrcPos;
|
|
EndChildNode;
|
|
ReadNextAtom;
|
|
end else if not AtomIsChar(';') then begin
|
|
// functions without statements are external and must end with a semicolon
|
|
RaiseExpectedButAtomFound(';');
|
|
end;
|
|
NeedEnd:=false;
|
|
end;
|
|
end else if AtomIsChar('[') then begin
|
|
// read array brackets
|
|
while AtomIsChar('[') do begin
|
|
ReadTilBracketClose(true);
|
|
ReadNextAtom;
|
|
end;
|
|
end;
|
|
|
|
// read initial constant
|
|
if AtomIsChar('=') then begin
|
|
if CurNode.HasParentOfType(ccnTypedef) then
|
|
RaiseException('typedef can not have an initial value');
|
|
ReadNextAtom;
|
|
ReadConstant;
|
|
ReadNextAtom;
|
|
NeedEnd:=true;
|
|
end;
|
|
|
|
// sanity check
|
|
if (SrcPos<=SrcLen) and NeedEnd
|
|
and not (AtomIsChar(';') or AtomIsChar(',') or AtomIsChar(')')) then
|
|
RaiseExpectedButAtomFound('"end of variable"');
|
|
|
|
UndoReadNextAtom;
|
|
|
|
EndChildNode;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.ReadParameterList;
|
|
// start on (, end on )
|
|
var
|
|
StartPos: LongInt;
|
|
TypePos: Integer;
|
|
NamePos: Integer;
|
|
begin
|
|
CreateChildNode(ccnFuncParamList);
|
|
StartPos:=AtomStart;
|
|
repeat
|
|
ReadNextAtom;
|
|
if AtomStart>SrcLen then begin
|
|
// missing closing bracket
|
|
AtomStart:=StartPos;
|
|
SrcPos:=AtomStart+1;
|
|
RaiseException('closing bracket not found');
|
|
end;
|
|
if AtomIsChar(')') then break;
|
|
if AtomIsChar(',') then
|
|
RaiseExpectedButAtomFound('parameter type');
|
|
// read parameter
|
|
CreateChildNode(ccnFuncParameter);
|
|
// examples:
|
|
// a a is the type
|
|
// a b a is type, b is name
|
|
// signed a a is the type
|
|
TypePos:=0;
|
|
NamePos:=0;
|
|
repeat
|
|
if AtomStart>SrcLen then break;
|
|
if AtomIsChar(')') then begin
|
|
// end of parameter list
|
|
break;
|
|
end else if AtomIs('[') then
|
|
ReadTilBracketClose(true)
|
|
else if AtomIsChar(',') then
|
|
break
|
|
else if IsIdentStartChar[Src[AtomStart]] then begin
|
|
if NamePos>0 then
|
|
RaiseExpectedButAtomFound(', or )');
|
|
if AtomIs('signed') or AtomIs('unsigned') or AtomIs('const') then
|
|
begin
|
|
// modifier
|
|
end else if AtomIs('short') or AtomIs('long') then begin
|
|
// modifier or type
|
|
TypePos:=AtomStart;
|
|
ReadNextAtom;
|
|
if AtomIs(')') or AtomIs(',') or AtomIs('(') then begin
|
|
// type
|
|
end else begin
|
|
// modifier
|
|
TypePos:=0;
|
|
end;
|
|
UndoReadNextAtom;
|
|
end else begin
|
|
// type or name
|
|
if TypePos<1 then begin
|
|
// type
|
|
TypePos:=AtomStart;
|
|
end else begin
|
|
// name
|
|
NamePos:=AtomStart;
|
|
CreateChildNode(ccnName);
|
|
EndChildNode;
|
|
end;
|
|
end;
|
|
end else if AtomIsChar('=') then begin
|
|
if TypePos<1 then
|
|
RaiseExpectedButAtomFound('type');
|
|
ReadNextAtom;
|
|
ReadConstant;
|
|
end;
|
|
CurNode.EndPos:=SrcPos;
|
|
ReadNextAtom;
|
|
until false;
|
|
EndChildNode;
|
|
until AtomIsChar(')');
|
|
CurNode.EndPos:=SrcPos;
|
|
EndChildNode;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.RaiseException(const AMessage: string; ReportPos: integer);
|
|
begin
|
|
LastErrorMsg:=AMessage;
|
|
LastErrorPos:=AtomStart;
|
|
LastErrorReportPos:=LastErrorPos;
|
|
if ReportPos>0 then
|
|
LastErrorReportPos:=ReportPos;
|
|
CloseNodes;
|
|
raise ECCodeParserException.Create(Self,AMessage);
|
|
end;
|
|
|
|
procedure TCCodeParserTool.RaiseExpectedButAtomFound(const AToken: string;
|
|
ReportPos: integer);
|
|
begin
|
|
RaiseException(AToken+' expected, but '+GetAtom+' found',ReportPos);
|
|
end;
|
|
|
|
constructor TCCodeParserTool.Create;
|
|
begin
|
|
Tree:=TCodeTree.Create;
|
|
InitCCOdeKeyWordLists;
|
|
VisibleEditorLines:=25;
|
|
JumpCentered:=true;
|
|
CursorBeyondEOL:=true;
|
|
end;
|
|
|
|
destructor TCCodeParserTool.Destroy;
|
|
begin
|
|
FreeAndNil(Tree);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.Clear;
|
|
begin
|
|
Tree.Clear;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.Parse;
|
|
begin
|
|
Parse(Code);
|
|
end;
|
|
|
|
procedure TCCodeParserTool.Parse(aCode: TCodeBuffer);
|
|
begin
|
|
if (Code=aCode) and (not UpdateNeeded) then
|
|
exit;
|
|
Code:=aCode;
|
|
InitParser;
|
|
repeat
|
|
ReadNextAtom;
|
|
if SrcPos<=SrcLen then begin
|
|
FDefaultTokenList.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart);
|
|
end else begin
|
|
break;
|
|
end;
|
|
until false;
|
|
if (CurNode=nil) or (CurNode.Desc<>ccnRoot) then
|
|
RaiseException('TCCodeParserTool.Parse: internal parser error');
|
|
EndChildNode;
|
|
end;
|
|
|
|
function TCCodeParserTool.UpdateNeeded: boolean;
|
|
begin
|
|
Result:=true;
|
|
if (Code=nil) or (Tree=nil) or (Tree.Root=nil) then exit;
|
|
if Code.ChangeStep<>ParseChangeStep then exit;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TCCodeParserTool.FindDeepestNodeAtPos(P: integer;
|
|
ExceptionOnNotFound: boolean): TCodeTreeNode; inline;
|
|
begin
|
|
Result:=FindDeepestNodeAtPos(Tree.Root,P,ExceptionOnNotFound);
|
|
end;
|
|
|
|
function TCCodeParserTool.FindDeepestNodeAtPos(StartNode: TCodeTreeNode;
|
|
P: integer; ExceptionOnNotFound: boolean): TCodeTreeNode;
|
|
|
|
procedure RaiseNoNodeFoundAtCursor;
|
|
begin
|
|
//DebugLn('RaiseNoNodeFoundAtCursor ',MainFilename);
|
|
RaiseException(ctsNoNodeFoundAtCursor);
|
|
end;
|
|
|
|
var
|
|
ChildNode: TCodeTreeNode;
|
|
Brother: TCodeTreeNode;
|
|
begin
|
|
{$IFDEF CheckNodeTool}CheckNodeTool(StartNode);{$ENDIF}
|
|
Result:=nil;
|
|
while StartNode<>nil do begin
|
|
//DebugLn('SearchInNode ',NodeDescriptionAsString(ANode.Desc),
|
|
//',',ANode.StartPos,',',ANode.EndPos,', p=',p,
|
|
//' "',copy(Src,ANode.StartPos,4),'" - "',copy(Src,ANode.EndPos-5,4),'"');
|
|
if (StartNode.StartPos<=P)
|
|
and ((StartNode.EndPos>P) or (StartNode.EndPos<1)) then begin
|
|
// StartNode contains P
|
|
Result:=StartNode;
|
|
// -> search for a child that contains P
|
|
Brother:=StartNode;
|
|
while (Brother<>nil)
|
|
and (Brother.StartPos<=P) do begin
|
|
// brother also contains P
|
|
if Brother.FirstChild<>nil then begin
|
|
ChildNode:=FindDeepestNodeAtPos(Brother.FirstChild,P,false);
|
|
if ChildNode<>nil then begin
|
|
Result:=ChildNode;
|
|
exit;
|
|
end;
|
|
end;
|
|
Brother:=Brother.NextBrother;
|
|
end;
|
|
break;
|
|
end else begin
|
|
// search in next node
|
|
StartNode:=StartNode.NextBrother;
|
|
end;
|
|
end;
|
|
if (Result=nil) and ExceptionOnNotFound then begin
|
|
MoveCursorToPos(P);
|
|
RaiseNoNodeFoundAtCursor;
|
|
end;
|
|
end;
|
|
|
|
function TCCodeParserTool.CaretToCleanPos(Caret: TCodeXYPosition; out
|
|
CleanPos: integer): integer;
|
|
begin
|
|
CleanPos:=0;
|
|
if Caret.Code<>Code then
|
|
exit(-1);
|
|
Code.LineColToPosition(Caret.Y,Caret.X,CleanPos);
|
|
if (CleanPos>=1) then
|
|
Result:=0
|
|
else
|
|
Result:=-2; // x,y beyond source
|
|
end;
|
|
|
|
function TCCodeParserTool.CleanPosToCodePos(CleanPos: integer; out
|
|
CodePos: TCodePosition): boolean;
|
|
begin
|
|
CodePos.Code:=Code;
|
|
CodePos.P:=CleanPos;
|
|
Result:=(Code<>nil) and (CleanPos>0) and (CleanPos<Code.SourceLength);
|
|
end;
|
|
|
|
function TCCodeParserTool.CleanPosToCaret(CleanPos: integer; out
|
|
Caret: TCodeXYPosition): boolean;
|
|
begin
|
|
Caret.Code:=Code;
|
|
Code.AbsoluteToLineCol(CleanPos,Caret.Y,Caret.X);
|
|
Result:=CleanPos>0;
|
|
end;
|
|
|
|
function TCCodeParserTool.CleanPosToCaretAndTopLine(CleanPos: integer; out
|
|
Caret: TCodeXYPosition; out NewTopLine: integer): boolean;
|
|
begin
|
|
Caret:=CleanCodeXYPosition;
|
|
NewTopLine:=0;
|
|
Result:=CleanPosToCaret(CleanPos,Caret);
|
|
if Result then begin
|
|
if JumpCentered then begin
|
|
NewTopLine:=Caret.Y-(VisibleEditorLines shr 1);
|
|
if NewTopLine<1 then NewTopLine:=1;
|
|
end else
|
|
NewTopLine:=Caret.Y;
|
|
end;
|
|
end;
|
|
|
|
function TCCodeParserTool.CleanPosToStr(CleanPos: integer): string;
|
|
var
|
|
CodePos: TCodeXYPosition;
|
|
begin
|
|
if CleanPosToCaret(CleanPos,CodePos) then
|
|
Result:='y='+IntToStr(CodePos.Y)+',x='+IntToStr(CodePos.X)
|
|
else
|
|
Result:='y=?,x=?';
|
|
end;
|
|
|
|
function TCCodeParserTool.MainFilename: string;
|
|
begin
|
|
Result:=Code.Filename;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.MoveCursorToPos(p: integer);
|
|
begin
|
|
SrcPos:=p;
|
|
AtomStart:=p;
|
|
LastAtomStart:=0;
|
|
LastSrcPos:=0;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.MoveCursorToNode(Node: TCodeTreeNode);
|
|
begin
|
|
MoveCursorToPos(Node.StartPos);
|
|
end;
|
|
|
|
procedure TCCodeParserTool.ReadNextAtom;
|
|
begin
|
|
//DebugLn(['TCCodeParserTool.ReadNextAtom START ',AtomStart,'-',SrcPos,' ',Src[SrcPos]]);
|
|
LastSrcPos:=SrcPos;
|
|
LastAtomStart:=AtomStart;
|
|
repeat
|
|
ReadRawNextCAtom(Src,SrcPos,AtomStart);
|
|
until (SrcPos>SrcLen) or (not (Src[AtomStart] in [#10,#13]));
|
|
{$IFDEF VerboseCCodeParser}
|
|
DebugLn(['TCCodeParserTool.ReadNextAtom END ',AtomStart,'-',SrcPos,' "',copy(Src,AtomStart,SrcPos-AtomStart),'"']);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TCCodeParserTool.ReadNextAtomSkipDirectives;
|
|
begin
|
|
//DebugLn(['TCCodeParserTool.ReadNextAtom START ',AtomStart,'-',SrcPos,' ',Src[SrcPos]]);
|
|
LastSrcPos:=SrcPos;
|
|
LastAtomStart:=AtomStart;
|
|
repeat
|
|
ReadRawNextCAtom(Src,SrcPos,AtomStart);
|
|
if (SrcPos>SrcLen) then break;
|
|
if Src[AtomStart]='#' then begin
|
|
ReadTilCLineEnd(Src,SrcPos);
|
|
if (SrcPos>SrcLen) then break;
|
|
end;
|
|
until (not (Src[AtomStart] in [#10,#13]));
|
|
{$IFDEF VerboseCCodeParser}
|
|
DebugLn(['TCCodeParserTool.ReadNextAtom END ',AtomStart,'-',SrcPos,' "',copy(Src,AtomStart,SrcPos-AtomStart),'"']);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TCCodeParserTool.UndoReadNextAtom;
|
|
begin
|
|
if LastSrcPos>0 then begin
|
|
SrcPos:=LastSrcPos;
|
|
AtomStart:=LastAtomStart;
|
|
LastSrcPos:=0;
|
|
LastAtomStart:=0;
|
|
end else begin
|
|
SrcPos:=AtomStart;
|
|
end;
|
|
end;
|
|
|
|
function TCCodeParserTool.ReadTilBracketClose(
|
|
ExceptionOnNotFound: boolean): boolean;
|
|
// AtomStart must be on bracket open
|
|
// after reading AtomStart is on closing bracket
|
|
var
|
|
CloseBracket: Char;
|
|
StartPos: LongInt;
|
|
begin
|
|
case Src[AtomStart] of
|
|
'{': CloseBracket:='}';
|
|
'[': CloseBracket:=']';
|
|
'(': CloseBracket:=')';
|
|
'<': CloseBracket:='>';
|
|
else
|
|
if ExceptionOnNotFound then
|
|
RaiseExpectedButAtomFound('(');
|
|
exit(false);
|
|
end;
|
|
StartPos:=AtomStart;
|
|
{$IFOPT R+}{$DEFINE RangeChecking}{$ENDIF}
|
|
{$R-}
|
|
repeat
|
|
ReadRawNextCAtom(Src,SrcPos,AtomStart);
|
|
if AtomStart>SrcLen then begin
|
|
AtomStart:=StartPos;
|
|
SrcPos:=AtomStart+1;
|
|
if ExceptionOnNotFound then
|
|
RaiseException('closing bracket not found');
|
|
exit;
|
|
end;
|
|
case Src[AtomStart] of
|
|
'{','(','[':
|
|
// skip nested bracketss
|
|
begin
|
|
if not ReadTilBracketClose(ExceptionOnNotFound) then
|
|
exit;
|
|
end;
|
|
else
|
|
if Src[AtomStart]=CloseBracket then exit(true);
|
|
end;
|
|
until false;
|
|
{$IFDEF RangeChecking}{$R+}{$UNDEF RangeChecking}{$ENDIF}
|
|
end;
|
|
|
|
function TCCodeParserTool.AtomIs(const s: shortstring): boolean;
|
|
var
|
|
len: Integer;
|
|
i: Integer;
|
|
begin
|
|
len:=length(s);
|
|
if (len<>SrcPos-AtomStart) then exit(false);
|
|
if SrcPos>SrcLen then exit(false);
|
|
for i:=1 to len do
|
|
if Src[AtomStart+i-1]<>s[i] then exit(false);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCCodeParserTool.AtomIsChar(const c: char): boolean;
|
|
begin
|
|
if SrcPos-AtomStart<>1 then exit(false);
|
|
if SrcPos>SrcLen then exit(false);
|
|
if Src[AtomStart]<>c then exit(false);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCCodeParserTool.UpAtomIs(const s: shortstring): boolean;
|
|
var
|
|
len: Integer;
|
|
i: Integer;
|
|
begin
|
|
len:=length(s);
|
|
if (len<>SrcPos-AtomStart) then exit(false);
|
|
if SrcPos>SrcLen then exit(false);
|
|
for i:=1 to len do
|
|
if UpChars[Src[AtomStart+i-1]]<>s[i] then exit(false);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCCodeParserTool.AtomIsIdentifier: boolean;
|
|
var
|
|
p: Integer;
|
|
begin
|
|
if (AtomStart>=SrcPos) then exit(false);
|
|
if (SrcPos>SrcLen) or (SrcPos-AtomStart>255) then exit(false);
|
|
if not IsIdentStartChar[Src[AtomStart]] then exit(false);
|
|
p:=AtomStart+1;
|
|
while (p<SrcPos) do begin
|
|
if not IsIdentChar[Src[p]] then exit(false);
|
|
inc(p);
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCCodeParserTool.AtomIsStringConstant: boolean;
|
|
begin
|
|
Result:=(AtomStart<SrcLen) and (Src[AtomStart]='"');
|
|
end;
|
|
|
|
function TCCodeParserTool.LastAtomIs(const s: shortstring): boolean;
|
|
var
|
|
len: Integer;
|
|
i: Integer;
|
|
begin
|
|
if LastAtomStart<=LastSrcPos then exit(false);
|
|
len:=length(s);
|
|
if (len<>LastSrcPos-LastAtomStart) then exit(false);
|
|
if LastSrcPos>SrcLen then exit(false);
|
|
for i:=1 to len do
|
|
if Src[LastAtomStart+i-1]<>s[i] then exit(false);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCCodeParserTool.GetLastAtom: string;
|
|
begin
|
|
Result:=copy(Src,LastAtomStart,LastSrcPos-LastAtomStart);
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractCode(StartPos, EndPos: integer;
|
|
WithDirectives: boolean): string;
|
|
var
|
|
s: string;
|
|
p: integer;
|
|
|
|
procedure ReadIt;
|
|
var
|
|
l: Integer;
|
|
NextChar: Char;
|
|
LastChar: Char;
|
|
begin
|
|
MoveCursorToPos(StartPos);
|
|
p:=1;
|
|
LastChar:=' ';
|
|
repeat
|
|
// read next token
|
|
if WithDirectives then
|
|
ReadNextAtom
|
|
else
|
|
ReadNextAtomSkipDirectives;
|
|
if (AtomStart>=EndPos) then break;
|
|
|
|
NextChar:=Src[AtomStart];
|
|
if IsIdentChar[LastChar] and IsIdentStartChar[NextChar] then begin
|
|
// add space
|
|
if s<>'' then
|
|
s[p]:=' ';
|
|
inc(p);
|
|
end;
|
|
// add token
|
|
l:=SrcPos-AtomStart;
|
|
if s<>'' then begin
|
|
// copy token
|
|
System.Move(Src[AtomStart],s[p],SrcPos-AtomStart);
|
|
end;
|
|
inc(p,l);
|
|
// remember last char
|
|
LastChar:=Src[SrcPos-1];
|
|
until false;
|
|
end;
|
|
|
|
begin
|
|
if EndPos>SrcLen then EndPos:=SrcLen+1;
|
|
// first read and compute needed length
|
|
ReadIt;
|
|
// allocate space and copy tokens
|
|
SetLength(s,p-1);
|
|
ReadIt;
|
|
Result:=s;
|
|
end;
|
|
|
|
function TCCodeParserTool.GetFirstNameNode(Node: TCodeTreeNode): TCodeTreeNode;
|
|
begin
|
|
Result:=Node.FirstChild;
|
|
while (Result<>nil) and (Result.Desc<>ccnName) do Result:=Result.NextBrother;
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractVariableName(VarNode: TCodeTreeNode): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
begin
|
|
NameNode:=GetFirstNameNode(VarNode);
|
|
if (NameNode=nil) then
|
|
Result:=''
|
|
else
|
|
Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractVariableType(VarNode: TCodeTreeNode;
|
|
WithDirectives: boolean): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
s: String;
|
|
begin
|
|
NameNode:=GetFirstNameNode(VarNode);
|
|
if (NameNode=nil) then
|
|
Result:=''
|
|
else begin
|
|
Result:=ExtractCode(VarNode.StartPos,NameNode.StartPos,WithDirectives);
|
|
if (NameNode.NextBrother<>nil)
|
|
and (NameNode.NextBrother.Desc=ccnConstant) then begin
|
|
// a variable with an initial value
|
|
// omit the constant
|
|
s:=ExtractCode(NameNode.EndPos,NameNode.NextBrother.StartPos,
|
|
WithDirectives);
|
|
s:=copy(s,1,length(s)-1);
|
|
Result:=Result+s;
|
|
end else begin
|
|
Result:=Result+ExtractCode(NameNode.EndPos,VarNode.EndPos,
|
|
WithDirectives);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractFunctionName(FuncNode: TCodeTreeNode): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
begin
|
|
NameNode:=GetFirstNameNode(FuncNode);
|
|
if (NameNode=nil) then
|
|
Result:=''
|
|
else
|
|
Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractFunctionType(FuncNode: TCodeTreeNode;
|
|
WithDirectives: boolean): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
begin
|
|
NameNode:=GetFirstNameNode(FuncNode);
|
|
if (NameNode=nil) then begin
|
|
Result:='';
|
|
exit;
|
|
end;
|
|
Result:=ExtractCode(FuncNode.StartPos,NameNode.StartPos,WithDirectives);
|
|
if (NameNode.NextBrother<>nil)
|
|
and (NameNode.NextBrother.Desc=ccnFuncParamList) then begin
|
|
// The name is in between.
|
|
// The type is result type + parameter list
|
|
Result:=Result+ExtractCode(NameNode.EndPos,NameNode.NextBrother.EndPos,
|
|
WithDirectives);
|
|
end else begin
|
|
Result:=Result+ExtractCode(NameNode.EndPos,FuncNode.EndPos,
|
|
WithDirectives);
|
|
end;
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractFunctionResultType(FuncNode: TCodeTreeNode;
|
|
WithDirectives: boolean): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
begin
|
|
NameNode:=GetFirstNameNode(FuncNode);
|
|
if (NameNode=nil) then begin
|
|
Result:='';
|
|
exit;
|
|
end;
|
|
|
|
// skip function modifiers
|
|
MoveCursorToNode(FuncNode);
|
|
repeat
|
|
ReadNextAtom;
|
|
if AtomStart>=NameNode.StartPos then break;
|
|
if not IsCCodeFunctionModifier.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart)
|
|
then
|
|
break;
|
|
until false;
|
|
|
|
Result:=ExtractCode(AtomStart,NameNode.StartPos,WithDirectives);
|
|
if (NameNode.NextBrother<>nil)
|
|
and (NameNode.NextBrother.Desc=ccnFuncParamList) then begin
|
|
// The name is in between.
|
|
Result:=Result+ExtractCode(NameNode.EndPos,NameNode.NextBrother.StartPos,
|
|
WithDirectives);
|
|
end else begin
|
|
Result:=Result+ExtractCode(NameNode.EndPos,FuncNode.EndPos,
|
|
WithDirectives);
|
|
end;
|
|
end;
|
|
|
|
function TCCodeParserTool.IsPointerToFunction(FuncNode: TCodeTreeNode
|
|
): boolean;
|
|
// for example: int *(*fp)();
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
begin
|
|
NameNode:=FuncNode.FirstChild;
|
|
if (NameNode=nil) or (NameNode.Desc<>ccnName) then exit(false);
|
|
MoveCursorToNode(FuncNode);
|
|
repeat
|
|
ReadNextAtom;
|
|
if AtomStart>SrcLen then exit;
|
|
if AtomIs('(') then exit(true);
|
|
if (IsIdentStartChar[Src[AtomStart]])
|
|
or (AtomIs('*')) then begin
|
|
// skip words and *
|
|
end else begin
|
|
break;
|
|
end;
|
|
until AtomStart>=NameNode.StartPos;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractParameterName(ParamNode: TCodeTreeNode
|
|
): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
begin
|
|
NameNode:=GetFirstNameNode(ParamNode);
|
|
if (NameNode=nil) then
|
|
Result:=''
|
|
else
|
|
Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractParameterType(ParamNode: TCodeTreeNode;
|
|
WithDirectives: boolean): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
s: String;
|
|
ConstantNode: TCodeTreeNode;
|
|
begin
|
|
NameNode:=GetFirstNameNode(ParamNode);
|
|
if (ParamNode.LastChild<>nil)
|
|
and (ParamNode.LastChild.Desc=ccnConstant) then
|
|
ConstantNode:=ParamNode.LastChild
|
|
else
|
|
ConstantNode:=nil;
|
|
if (NameNode=nil) then begin
|
|
if ConstantNode<>nil then begin
|
|
// a parameter with an initial value
|
|
// omit the constant
|
|
Result:=ExtractCode(ParamNode.StartPos,ConstantNode.StartPos,WithDirectives);
|
|
Result:=copy(Result,1,length(Result)-1);
|
|
end else begin
|
|
Result:=ExtractCode(ParamNode.StartPos,ParamNode.EndPos,WithDirectives);
|
|
end;
|
|
end else begin
|
|
Result:=ExtractCode(ParamNode.StartPos,NameNode.StartPos,WithDirectives);
|
|
if (NameNode.NextBrother<>nil)
|
|
and (NameNode.NextBrother.Desc=ccnConstant) then begin
|
|
// a parameter with an initial value
|
|
// omit the constant
|
|
s:=ExtractCode(NameNode.EndPos,NameNode.NextBrother.StartPos,
|
|
WithDirectives);
|
|
s:=copy(s,1,length(s)-1);
|
|
Result:=Result+s;
|
|
end else begin
|
|
Result:=Result+ExtractCode(NameNode.EndPos,ParamNode.EndPos,
|
|
WithDirectives);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractEnumBlockName(EnumBlockNode: TCodeTreeNode
|
|
): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
begin
|
|
if (EnumBlockNode.FirstChild<>nil)
|
|
and (EnumBlockNode.FirstChild.Desc=ccnName) then begin
|
|
NameNode:=EnumBlockNode.FirstChild;
|
|
Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
|
|
end else begin
|
|
Result:='';
|
|
end;
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractEnumIDName(EnumIDNode: TCodeTreeNode): string;
|
|
begin
|
|
Result:=GetIdentifier(@Src[EnumIDNode.StartPos]);
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractEnumIDValue(EnumIDNode: TCodeTreeNode;
|
|
WithDirectives: boolean): string;
|
|
var
|
|
ConstantNode: TCodeTreeNode;
|
|
begin
|
|
ConstantNode:=EnumIDNode.FirstChild;
|
|
if (ConstantNode=nil)
|
|
or (ConstantNode.Desc<>ccnConstant) then
|
|
Result:=''
|
|
else
|
|
Result:=ExtractCode(ConstantNode.StartPos,ConstantNode.EndPos,
|
|
WithDirectives);
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractStructName(StructNode: TCodeTreeNode): string;
|
|
var
|
|
NameNode: TCodeTreeNode;
|
|
begin
|
|
NameNode:=StructNode.FirstChild;
|
|
if (NameNode<>nil) and (NameNode.Desc=ccnName) then
|
|
Result:=GetIdentifier(@Src[NameNode.StartPos])
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TCCodeParserTool.ExtractTypedefName(TypedefNode: TCodeTreeNode
|
|
): string;
|
|
var
|
|
Node: TCodeTreeNode;
|
|
begin
|
|
Node:=TypedefNode.LastChild;
|
|
while (Node<>nil) and (Node.Desc<>ccnName) do
|
|
Node:=Node.PriorBrother;
|
|
if Node=nil then
|
|
Result:=''
|
|
else
|
|
Result:=GetIdentifier(@Src[Node.StartPos]);
|
|
end;
|
|
|
|
function TCCodeParserTool.GetAtom: string;
|
|
begin
|
|
Result:=copy(Src,AtomStart,SrcPos-AtomStart);
|
|
end;
|
|
|
|
procedure TCCodeParserTool.Replace(FromPos, ToPos: integer; const NewSrc: string
|
|
);
|
|
var
|
|
Node: TCodeTreeNode;
|
|
DiffPos: Integer;
|
|
begin
|
|
{$IFDEF VerboseCCodeParser}
|
|
DebugLn(['TCCodeParserTool.Replace ',FromPos,'-',ToPos,' Old="',copy(Src,FromPos,ToPos-FromPos),'" New="',NewSrc,'"']);
|
|
{$ENDIF}
|
|
IncreaseChangeStep;
|
|
Code.Replace(FromPos,ToPos-FromPos,NewSrc);
|
|
Src:=Code.Source;
|
|
SrcLen:=length(Src);
|
|
// update positions
|
|
DiffPos:=length(NewSrc)-(ToPos-FromPos);
|
|
if DiffPos<>0 then begin
|
|
Node:=Tree.Root;
|
|
while Node<>nil do begin
|
|
AdjustPositionAfterInsert(Node.StartPos,true,FromPos,ToPos,DiffPos);
|
|
AdjustPositionAfterInsert(Node.EndPos,false,FromPos,ToPos,DiffPos);
|
|
Node:=Node.Next;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.IncreaseChangeStep;
|
|
begin
|
|
if FChangeStep<>$7fffffff then
|
|
inc(FChangeStep)
|
|
else
|
|
FChangeStep:=-$7fffffff;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.WriteDebugReport;
|
|
var
|
|
Node: TCodeTreeNode;
|
|
begin
|
|
DebugLn(['TCCodeParserTool.WriteDebugReport ']);
|
|
if Tree<>nil then begin
|
|
Node:=Tree.Root;
|
|
while Node<>nil do begin
|
|
DebugLn([GetIndentStr(Node.GetLevel*2)+CCNodeDescAsString(Node.Desc)]);
|
|
Node:=Node.Next;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCCodeParserTool.CheckNodeTool(Node: TCodeTreeNode);
|
|
|
|
procedure RaiseForeignNode;
|
|
begin
|
|
RaiseCatchableException('TCCodeParserTool.CheckNodeTool '+DbgSName(Self)+' '+CCNodeDescAsString(Node.Desc));
|
|
end;
|
|
|
|
begin
|
|
if Node=nil then exit;
|
|
while Node.Parent<>nil do Node:=Node.Parent;
|
|
while Node.PriorBrother<>nil do Node:=Node.PriorBrother;
|
|
if (Tree=nil) or (Tree.Root<>Node) then
|
|
RaiseForeignNode;
|
|
end;
|
|
|
|
procedure InternalFinal;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if KeyWordLists<>nil then begin
|
|
for i:=0 to KeyWordLists.Count-1 do
|
|
TObject(KeyWordLists[i]).Free;
|
|
FreeAndNil(KeyWordLists);
|
|
end;
|
|
end;
|
|
|
|
finalization
|
|
InternalFinal;
|
|
|
|
end.
|
|
|