# Saturday, January 14, 2006

Updated FLL with more functionality
I don't have time (it's late) to comment too much on the changes I've made to the regexp.fll. I significantly enhanced the functionality of the regexp() function when it is splitting. You can now split results into an array or a cursor. I also fixed a couple of bugs in the code having to do with the splitting feature. In any event, the documentation from my previous blog entry is still good, but now you can send in parameter nFunctionType = 4 and you will get a cursor. Also, if you are using nFunctionType = 3 or 4, you can specify a Visual FoxPro command for the regular expression engine to execute each time a match is found (think of it as a callback function of sorts).

An XML parsing example
This library is really shaping up, but I think there are lots of other possibilities that haven't been explored yet (Comment here on the blog or send me any good ideas for functionality you might have - I'd love to hear them). One thing I knew the library could do is parse XML. Using some regular expressions I found freely available on the internet, I created some sample code (see below) to show of the wonderous abilities of regular expressions as well as the new functionality in the FLL.

More to come
As cool as this may be, I'm still not really doing them (regular expressions or this FLL) justice. When I get more time, I'll be back to update the documentation and show how this FLL gives us abilities in Visual FoxPro that we didn't ever have before. Here's the download and some example code (the ParseXML function is the guts of it and you need the newest regexp.fll to run this). Enjoy.

Download the regexp.fll (63 KB approx)

*!* Example of using the ParseXML function
#DEFINE CREATEANARRAY 2
#DEFINE CREATEACURSOR 3

LOCAL lcArrayToCreate, lcCursorToCreate, lnTotal, lnCounter
*!* Here's some sample XML
*!* just to demonstrate
TEXT TO lcSampleXML NOSHOW PRETEXT 7
<?xml version="1.0"?>
<samples>
<sample>Visual FoxPro Rocks!</sample>
<sample>Does more before breakfast</sample>
<sample>than other languages do all day</sample>
</samples>
ENDTEXT

*!* Or you can try this using an XML file
*!* You can get some sample XML files at
*!* ftp://sunsite.unc.edu/pub/sun-info/standards/xml/eg/shakespeare.1.01.xml.zip
*!* Once you have extracted them then
*!* comment out the TEXT/ENDTEXT code above
*!* lastly you can something like the following
*!* to load the example XML files
*!* lcSampleXML = FILETOSTR(GETFILE())

*!* Create an array
DIMENSION aryXMLDoc(1)
lcArrayToCreate = "aryXMLDoc"
lnTotal = ParseXML(lcSampleXML, lcArrayToCreate, CREATEANARRAY)
CLEAR
FOR lnCounter = 1 TO lnTotal
    ?aryXMLDoc(lnCounter)
ENDFOR
MESSAGEBOX("Parsed XML has been output to the screen from the array." ;
        + CHR(13) + "Click OK to continue with the next example.", ;
        64, "XML Parsing Complete")

*!* Create a cursor
lcCursorToCreate = "crsXMLDoc"
lnTotal = ParseXML(lcSampleXML, lcCursorToCreate, CREATEACURSOR)
IF lnTotal > 0
    GO TOP IN (lcCursorToCreate)
    BROWSE
ENDIF

MESSAGEBOX("Click OK to run the final example.", ;
        64, "Begin Final XML Parsing Example")
        
*!* Create a cursor, but also execute some
*!* VFP code as the C++ parser runs.
*!* This can be used to manipulate the data
*!* as it is retrieved or whatever.
*!* The C++ parser in the FLL
*!* will execute whatever command you tell it to
lcCursorToCreate = "crsXMLDoc"
lcVFPCommand = "DO AppendVFPRocks"
lnTotal = ParseXML(lcSampleXML, lcCursorToCreate, CREATEACURSOR, lcVFPCommand)
IF lnTotal > 0
    CLEAR
    GO TOP IN (lcCursorToCreate)
    SCAN ALL
        ? Splits
    ENDSCAN
ENDIF

**************************
PROCEDURE AppendVFPRocks
**************************
    Replace splits WITH "VISUAL FOXPRO ROCKS!" + splits
ENDPROC


*******************************
FUNCTION ParseXML (tcXML, tcName, tnType, tcCommand)
*******************************
*!* This Function is made possible by the work
*!* of Robert Cameron...
*******************************
*!* REX/Perl 1.0
*!* Robert D. Cameron "REX: XML Shallow Parsing with Regular Expressions",
*!* Technical Report TR 1998-17, School of Computing Science, Simon Fraser
*!* University, November, 1998.
*!* Copyright (c) 1998, Robert D. Cameron.
*!* The following code may be freely used and distributed provided that
*!* this copyright and citation notice remains intact and that modifications
*!* or additions are clearly identified.
*!* http://www.cs.sfu.ca/~cameron/REX.html
*******************************
*!* 01-14-2006: Translated and modified his expressions
*!* for use in Visual FoxPro with the regexp.fll
*!* by Craig Boyd http://www.sweetpotatosoftware.com/spsblog
*******************************

    LOCAL lcTextSE, lcUntilHyphen, ;
        lcUntil2Hyphens, lcCommentCE, lcUntilRSBs, ;
        lcCDATA_CE, lcS, lcNameStrt, lcNameChar, ;
        lcName, lcQuoteSE, lcDT_IdentSE, ;
        lcMarkupDeclCE, lcS1, lcUntilQMs, ;
        lcPI_Tail, lcDT_ItemSE, lcDocTypeCE, ;
        lcDeclCE, lcPI_CE, lcEndTagCE, lcAttValSE, ;
        lcElemTagCE, lcMarkupSPE, lcXML_SPE, ;
        lcExpression, lvReturn
    IF !("\REGEXP.FLL" $ SET("Library"))
        SET LIBRARY TO LOCFILE("regexp.fll", "FLL")
    ENDIF
    lcTextSE = "([^<]+"
    lcUntilHyphen = "[^-]*-"
    lcUntil2Hyphens = lcUntilHyphen + "(?:[^-]" + lcUntilHyphen + ")*-"
    lcCommentCE = lcUntil2Hyphens + ">?"
    lcUntilRSBs = "[^\]]*](?:[^\]]+])*]+"
    lcCDATA_CE = lcUntilRSBs + "(?:[^\]>]" + lcUntilRSBs + ")*>"
    lcS = "[ \n\t\r]+"
    lcNameStrt = "[A-Za-z_:]|[^\x00-\x7F]"
    lcNameChar = "[A-Za-z0-9_:.-]|[^\x00-\x7F]"
    lcName = "(?:" + lcNameStrt + ")(?:" + lcNameChar + ")*"
    lcQuoteSE = '"[^"]*"|' + "'[^']*'"
    lcDT_IdentSE = lcS + lcName + "
(?:" + lcS + "(?:" + lcName + "|" + lcQuoteSE + "))*"
    lcMarkupDeclCE = '(?:[^\]"
' + "'><]+|" + lcQuoteSE + ")*>"
    lcS1 = "[\n\r\t ]"
    lcUntilQMs = "[^?]*\?+"
    lcPI_Tail = "\?>|" + lcS1 + lcUntilQMs + "(?:[^>?]" + lcUntilQMs + ")*>"
    lcDT_ItemSE = "<(?:!(?:--" + lcUntil2Hyphens + ">|[^-]" + lcMarkupDeclCE + ")|\?" + lcName + "(?:" + lcPI_Tail + "))|%" + lcName + ";|" + lcS
    lcDocTypeCE = lcDT_IdentSE + "(?:" + lcS + ")?(?:\[(?:" + lcDT_ItemSE + ")*](?:" + lcS + ")?)?>?"
    lcDeclCE = "--(?:" + lcCommentCE + ")?|\[CDATA\[(?:" + lcCDATA_CE + ")?|DOCTYPE(?:" + lcDocTypeCE + ")?"
    lcPI_CE = lcName + "(?:" + lcPI_Tail + ")?"
    lcEndTagCE = lcName + "(?:" + lcS + ")?>?"
    lcAttValSE = '"[^<"]*"|' + "'[^<']*'"
    lcElemTagCE = lcName + "
(?:" + lcS + lcName + "(?:" + lcS + ")?=(?:" + lcS + ")?(?:" + lcAttValSE + "))*(?:" + lcS + ")?/?>?"
    lcMarkupSPE = "
<(?:!(?:" + lcDeclCE + ")?|\?(?:" + lcPI_CE + ")?|/(?:" + lcEndTagCE + ")?|(?:" + lcElemTagCE + ")?))"
    lcXML_SPE = lcTextSE + "
|" + lcMarkupSPE

    lcExpression = lcXML_SPE
    IF VARTYPE(tcCommand) = "
C"
        lvReturn = RegExp(tcXML, lcExpression, tnType, tcName, tcCommand)
    ELSE
        lvReturn = RegExp(tcXML, lcExpression, tnType, tcName)
    ENDIF
    RETURN (lvReturn)
ENDFUNC

Saturday, January 14, 2006 11:23:09 AM (GMT Standard Time, UTC+00:00)  #    Comments [6]

 

Archive

<January 2006>
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
2930311234