Переглянути джерело

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 -

sergei 16 роки тому
батько
коміт
538f82091a
1 змінених файлів з 60 додано та 72 видалено
  1. 60 72
      packages/fcl-xml/src/xpath.pp

+ 60 - 72
packages/fcl-xml/src/xpath.pp

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