XPath improvements:

+ Utility function TXPathScanner.SkipToken, saves some amount of typing.
* Allow TXPathLocationPathNode to have FFirstStep = nil, and don't create a redundant
  initial step while parsing.

git-svn-id: trunk@13253 -
This commit is contained in:
sergei 2009-06-09 22:52:16 +00:00
parent 2c3d6645be
commit 538f82091a

View File

@ -372,6 +372,7 @@ type
constructor Create(const AExpressionString: DOMString); constructor Create(const AExpressionString: DOMString);
function NextToken: TXPathToken; function NextToken: TXPathToken;
function PeekToken: TXPathToken; function PeekToken: TXPathToken;
function SkipToken(tok: TXPathToken): Boolean;
property CurToken: TXPathToken read FCurToken; property CurToken: TXPathToken read FCurToken;
property CurTokenString: DOMString read FCurTokenString; property CurTokenString: DOMString read FCurTokenString;
end; end;
@ -1274,6 +1275,7 @@ var
ResultNodeSet: TNodeSet; ResultNodeSet: TNodeSet;
LeftResult: TXPathVariable; LeftResult: TXPathVariable;
i: Integer; i: Integer;
Node: TDOMNode;
procedure EvaluateStep(AStep: TStep; AContextNode: TDOMNode); procedure EvaluateStep(AStep: TStep; AContextNode: TDOMNode);
var var
@ -1318,10 +1320,17 @@ begin
LeftResult.Release; LeftResult.Release;
end; end;
end end
else if FIsAbsolutePath and (AContext.ContextNode.NodeType <> DOCUMENT_NODE) then
EvaluateStep(FFirstStep, AContext.ContextNode.OwnerDocument)
else else
EvaluateStep(FFirstStep, AContext.ContextNode); begin
if FIsAbsolutePath and (AContext.ContextNode.NodeType <> DOCUMENT_NODE) then
Node := AContext.ContextNode.OwnerDocument
else
Node := AContext.ContextNode;
if Assigned(FFirstStep) then
EvaluateStep(FFirstStep, Node)
else
ResultNodeSet.Add(Node); // Assert(FIsAbsolutePath)
end;
except except
ResultNodeSet.Free; ResultNodeSet.Free;
raise; raise;
@ -1551,6 +1560,13 @@ begin
SetString(FCurTokenString, FTokenStart, FTokenLength); SetString(FCurTokenString, FTokenStart, FTokenLength);
end; end;
function TXPathScanner.SkipToken(tok: TXPathToken): Boolean; { inline? }
begin
Result := (FCurToken = tok);
if Result then
NextToken;
end;
function TXPathScanner.GetToken: TXPathToken; function TXPathScanner.GetToken: TXPathToken;
procedure GetNumber(HasDot: Boolean); procedure GetNumber(HasDot: Boolean);
@ -1566,16 +1582,22 @@ function TXPathScanner.GetToken: TXPathToken;
Result := tkNumber; Result := tkNumber;
end; end;
begin // TODO: no surrogate pairs/XML 1.1 support yet
if FCurToken = tkEndOfStream then function ScanNCName: Boolean;
begin begin
Result := tkEndOfStream; Result := Byte(FCurData^) in NamingBitmap[NamePages[hi(Word(FCurData^))]];
exit; if Result then
begin
FTokenLength := 1;
while Byte(FCurData[1]) in NamingBitmap[NamePages[$100+hi(Word(FCurData[1]))]] do
begin
Inc(FCurData);
Inc(FTokenLength);
end;
end;
end; end;
{ No, we cannot use a lookup table here, as future begin
versions will use WideStrings -sg }
// Skip whitespace // Skip whitespace
while (FCurData[0] < #255) and (char(ord(FCurData[0])) in [#9, #10, #13, ' ']) do while (FCurData[0] < #255) and (char(ord(FCurData[0])) in [#9, #10, #13, ' ']) do
Inc(FCurData); Inc(FCurData);
@ -1686,17 +1708,10 @@ begin
'|': '|':
Result := tkPipe; Result := tkPipe;
else else
// TODO: no surrogate pairs/XML 1.1 support yet if ScanNCName then
if Byte(FCurData^) in NamingBitmap[NamePages[hi(Word(FCurData^))]] then // TODO: must handle 'NCName:*' and 'NCName:NCName' here,
begin // these are single tokens which may not have whitespace inbetween.
FTokenLength := 1; Result := tkIdentifier
Result := tkIdentifier;
while Byte(FCurData[1]) in NamingBitmap[NamePages[$100+hi(Word(FCurData[1]))]] do
begin
Inc(FCurData);
Inc(FTokenLength);
end;
end
else else
Error(SScannerInvalidChar); Error(SScannerInvalidChar);
end; end;
@ -1719,9 +1734,8 @@ begin
I := 0; I := 0;
// accumulate nodes in local buffer, then add all at once // accumulate nodes in local buffer, then add all at once
// this reduces amount of ReallocMem's // this reduces amount of ReallocMem's
while CurToken = tkLeftSquareBracket do while SkipToken(tkLeftSquareBracket) do
begin begin
NextToken;
Buffer[I] := ParseOrExpr; Buffer[I] := ParseOrExpr;
Inc(I); Inc(I);
if I > High(Buffer) then if I > High(Buffer) then
@ -1729,9 +1743,8 @@ begin
AddNodes(Dest, Buffer); AddNodes(Dest, Buffer);
I := 0; I := 0;
end; end;
if CurToken <> tkRightSquareBracket then if not SkipToken(tkRightSquareBracket) then
Error(SParserExpectedRightSquareBracket); Error(SParserExpectedRightSquareBracket);
NextToken;
end; end;
if I > 0 then if I > 0 then
AddNodes(Dest, Slice(Buffer, I)); AddNodes(Dest, Slice(Buffer, I));
@ -1801,9 +1814,7 @@ begin
NextToken; // skip identifier and the '::' NextToken; // skip identifier and the '::'
NextToken; NextToken;
end end;
else if CurToken <> tkEndOfStream then
Dest.Axis := axisChild;
// Parse [7] NodeTest // Parse [7] NodeTest
if CurToken = tkAsterisk then // [37] NameTest, first case if CurToken = tkAsterisk then // [37] NameTest, first case
@ -1925,32 +1936,23 @@ end;
function TXPathScanner.ParseUnionExpr: TXPathExprNode; // [18] function TXPathScanner.ParseUnionExpr: TXPathExprNode; // [18]
begin begin
Result := ParsePathExpr; Result := ParsePathExpr;
while CurToken = tkPipe do while SkipToken(tkPipe) do
begin
NextToken;
Result := TXPathUnionNode.Create(Result, ParsePathExpr); Result := TXPathUnionNode.Create(Result, ParsePathExpr);
end;
end; end;
function TXPathScanner.ParsePathExpr: TXPathExprNode; // [19] function TXPathScanner.ParsePathExpr: TXPathExprNode; // [19]
var var
IsFunctionCall: Boolean;
CurStep, NextStep: TStep; CurStep, NextStep: TStep;
begin begin
// Try to detect wether a LocationPath [1] or a FilterExpr [20] follows Result := nil;
IsFunctionCall := False; CurStep := nil;
if (CurToken = tkIdentifier) and // Try to detect whether a LocationPath [1] or a FilterExpr [20] follows
if ((CurToken = tkIdentifier) and (PeekToken = tkLeftBracket) and
(CurTokenString <> 'comment') and (CurTokenString <> 'comment') and
(CurTokenString <> 'text') and (CurTokenString <> 'text') and
(CurTokenString <> 'processing-instruction') and (CurTokenString <> 'processing-instruction') and
(CurTokenString <> 'node') and (CurTokenString <> 'node')) or
(PeekToken = tkLeftBracket) then (CurToken in [tkDollar, tkLeftBracket, tkString, tkNumber]) then
IsFunctionCall := True;
Result := nil;
CurStep := nil;
if IsFunctionCall or (CurToken in
[tkDollar, tkLeftBracket, tkString, tkNumber]) then
begin begin
// second, third or fourth case of [19] // second, third or fourth case of [19]
Result := ParseFilterExpr; Result := ParseFilterExpr;
@ -1967,26 +1969,17 @@ begin
NextToken; NextToken;
end end
else if CurToken = tkSlash then else if CurToken = tkSlash then
begin
NextToken; NextToken;
// TODO: This looks unnecessary, but evaluate() should be fixed
if TXPathLocationPathNode(Result).FLeft = nil then while CurToken in [tkDot, tkDotDot, tkAt, tkAsterisk, tkIdentifier] do
begin begin
CurStep := TStep.Create(axisSelf, ntAnyNode); // axisChild is the default. ntAnyPrincipal is dummy.
TXPathLocationPathNode(Result).FFirstStep := CurStep; NextStep := TStep.Create(axisChild, ntAnyPrincipal);
end; if Assigned(CurStep) then
end; CurStep.NextStep := NextStep
else
repeat TXPathLocationPathNode(Result).FFirstStep := NextStep;
if CurToken <> tkEndOfStream then CurStep := NextStep;
begin
NextStep := TStep.Create(axisInvalid, ntAnyPrincipal); { args are dummy }
if Assigned(CurStep) then
CurStep.NextStep := NextStep
else
TXPathLocationPathNode(Result).FFirstStep := NextStep;
CurStep := NextStep;
end;
// Parse [4] Step // Parse [4] Step
ParseStep(CurStep); ParseStep(CurStep);
@ -2000,11 +1993,9 @@ begin
CurStep.NextStep := NextStep; CurStep.NextStep := NextStep;
CurStep := NextStep; CurStep := NextStep;
end end
else if CurToken <> tkSlash then else if not SkipToken(tkSlash) then
break break;
else end;
NextToken; // skip the slash
until False;
end; end;
function TXPathScanner.ParseFilterExpr: TXPathExprNode; // [20] function TXPathScanner.ParseFilterExpr: TXPathExprNode; // [20]
@ -2120,11 +2111,8 @@ var
NegCount: Integer; NegCount: Integer;
begin begin
NegCount := 0; NegCount := 0;
while CurToken = tkMinus do while SkipToken(tkMinus) do
begin
Inc(NegCount); Inc(NegCount);
NextToken;
end;
Result := ParseUnionExpr; Result := ParseUnionExpr;
if Odd(NegCount) then if Odd(NegCount) then