summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorptmcg <ptmcg@9bf210a0-9d2d-494c-87cf-cfb32e7dff7b>2014-10-11 05:11:47 +0000
committerptmcg <ptmcg@9bf210a0-9d2d-494c-87cf-cfb32e7dff7b>2014-10-11 05:11:47 +0000
commit455b3d02dec8a349646af26cb11666d88c38e541 (patch)
treea92d1f91004eecc97cfcf81c1d9f0c9968d3057f
parent8454ef5f2a7a6b4f1a04953f7bbc90fcb22b4a61 (diff)
downloadpyparsing-455b3d02dec8a349646af26cb11666d88c38e541.tar.gz
Minor mods to released examples
git-svn-id: svn://svn.code.sf.net/p/pyparsing/code/trunk@277 9bf210a0-9d2d-494c-87cf-cfb32e7dff7b
-rw-r--r--src/examples/datetimeParseActions.py47
-rw-r--r--src/examples/parsePythonValue.py5
-rw-r--r--src/examples/position.py55
-rw-r--r--src/examples/rangeCheck.py76
-rw-r--r--src/examples/shapes.py64
5 files changed, 245 insertions, 2 deletions
diff --git a/src/examples/datetimeParseActions.py b/src/examples/datetimeParseActions.py
new file mode 100644
index 0000000..38e660e
--- /dev/null
+++ b/src/examples/datetimeParseActions.py
@@ -0,0 +1,47 @@
+# parseActions.py
+#
+# A sample program a parser to match a date string of the form "YYYY/MM/DD",
+# and return it as a datetime, or raise an exception if not a valid date.
+#
+# Copyright 2012, Paul T. McGuire
+#
+from datetime import datetime
+from pyparsing import *
+
+# define an integer string, and a parse action to convert it
+# to an integer at parse time
+integer = Word(nums)
+def convertToInt(tokens):
+ # no need to test for validity - we can't get here
+ # unless tokens[0] contains all numeric digits
+ return int(tokens[0])
+integer.setParseAction(convertToInt)
+# or can be written as one line as
+#integer = Word(nums).setParseAction(lambda t: int(t[0]))
+
+# define a pattern for a year/month/day date
+date = integer('year') + '/' + integer('month') + '/' + integer('day')
+
+def convertToDatetime(s,loc,tokens):
+ try:
+ # note that the year, month, and day fields were already
+ # converted to ints from strings by the parse action defined
+ # on the integer expression above
+ return datetime(tokens.year, tokens.month, tokens.day)
+ except Exception as ve:
+ errmsg = "'%d/%d/%d' is not a valid date, %s" % \
+ (tokens.year, tokens.month, tokens.day, ve)
+ raise ParseException(s, loc, errmsg)
+date.setParseAction(convertToDatetime)
+
+
+def test(s):
+ try:
+ print(date.parseString(s))
+ except ParseException as pe:
+ print(pe)
+
+test("2000/1/1")
+test("2000/13/1") # invalid month
+test("1900/2/29") # 1900 was not a leap year
+test("2000/2/29") # but 2000 was
diff --git a/src/examples/parsePythonValue.py b/src/examples/parsePythonValue.py
index 1a9cd99..53c61fc 100644
--- a/src/examples/parsePythonValue.py
+++ b/src/examples/parsePythonValue.py
@@ -2,9 +2,10 @@
#
# Copyright, 2006, by Paul McGuire
#
-
+from __future__ import print_function
from pyparsing import *
+
cvtBool = lambda t:t[0]=='True'
cvtInt = lambda toks: int(toks[0])
cvtReal = lambda toks: float(toks[0])
@@ -40,7 +41,7 @@ tupleStr.setParseAction( cvtTuple )
listStr << (lbrack + Optional(delimitedList(listItem) +
Optional(Suppress(","))) + rbrack)
-listStr.setParseAction( cvtList, lambda t: t[0] )
+listStr.setParseAction(cvtList, lambda t: t[0])
dictEntry = Group( listItem + colon + listItem )
dictStr << (lbrace + Optional(delimitedList(dictEntry) + \
diff --git a/src/examples/position.py b/src/examples/position.py
new file mode 100644
index 0000000..984c018
--- /dev/null
+++ b/src/examples/position.py
@@ -0,0 +1,55 @@
+from pyparsing import *
+
+text = """Lorem ipsum dolor sit amet, consectetur adipisicing
+elit, sed do eiusmod tempor incididunt ut labore et dolore magna
+aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
+aute irure dolor in reprehenderit in voluptate velit esse cillum
+dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+cupidatat non proident, sunt in culpa qui officia deserunt
+mollit anim id est laborum"""
+
+# find all words beginning with a vowel
+vowels = "aeiouAEIOU"
+initialVowelWord = Word(vowels,alphas)
+
+# Unfortunately, searchString will advance character by character through
+# the input text, so it will detect that the initial "Lorem" is not an
+# initialVowelWord, but then it will test "orem" and think that it is. So
+# we need to add a do-nothing term that will match the words that start with
+# consonants, but we will just throw them away when we match them. The key is
+# that, in having been matched, the parser will skip over them entirely when
+# looking for initialVowelWords.
+consonants = ''.join(c for c in alphas if c not in vowels)
+initialConsWord = Word(consonants, alphas).suppress()
+
+# using scanString to locate where tokens are matched
+for t,start,end in (initialConsWord|initialVowelWord).scanString(text):
+ if t:
+ print(start,':', t[0])
+
+# add parse action to annotate the parsed tokens with their location in the
+# input string
+def addLocnToTokens(s,l,t):
+ t['locn'] = l
+ t['word'] = t[0]
+initialVowelWord.setParseAction(addLocnToTokens)
+
+for ivowelInfo in (initialConsWord | initialVowelWord).searchString(text):
+ if not ivowelInfo:
+ continue
+ print(ivowelInfo.locn, ':', ivowelInfo.word)
+
+
+# alternative - add an Empty that will save the current location
+def location(name):
+ return Empty().setParseAction(lambda s,l,t: t.__setitem__(name,l))
+locateInitialVowels = location("locn") + initialVowelWord("word")
+
+# search through the input text
+for ivowelInfo in (initialConsWord | locateInitialVowels).searchString(text):
+ if not ivowelInfo:
+ continue
+ print(ivowelInfo.locn, ':', ivowelInfo.word)
+
+
diff --git a/src/examples/rangeCheck.py b/src/examples/rangeCheck.py
new file mode 100644
index 0000000..35c9ef3
--- /dev/null
+++ b/src/examples/rangeCheck.py
@@ -0,0 +1,76 @@
+# rangeCheck.py
+#
+# A sample program showing how parse actions can convert parsed
+# strings into a data type or object, and to validate the parsed value.
+#
+# Copyright 2011, Paul T. McGuire
+#
+
+from pyparsing import Word, nums, Suppress, ParseException, empty, Optional
+from datetime import datetime
+
+def rangeCheck(minval=None, maxval=None):
+ # have to specify at least one range boundary
+ if minval is None and maxval is None:
+ raise ValueError("minval or maxval must be specified")
+
+ # set range testing function and error message depending on
+ # whether either or both min and max values are given
+ inRangeFn = {
+ (True, False) : lambda x : x <= maxval,
+ (False, True) : lambda x : minval <= x,
+ (False, False) : lambda x : minval <= x <= maxval,
+ }[minval is None, maxval is None]
+ outOfRangeMessage = {
+ (True, False) : "value %%s is greater than %s" % maxval,
+ (False, True) : "value %%s is less than %s" % minval,
+ (False, False) : "value %%s is not in the range (%s to %s)" % (minval,maxval),
+ }[minval is None, maxval is None]
+
+ # define the actual range checking parse action
+ def rangeCheckParseAction(string, loc, tokens):
+ parsedval = tokens[0]
+ if not inRangeFn(parsedval):
+ raise ParseException(string, loc, outOfRangeMessage % parsedval)
+
+ return rangeCheckParseAction
+
+# define the expressions for a date of the form YYYY/MM/DD or YYYY/MM (assumes YYYY/MM/01)
+integer = Word(nums).setName("integer")
+integer.setParseAction(lambda t:int(t[0]))
+
+month = integer.copy().addParseAction(rangeCheck(1,12))
+day = integer.copy().addParseAction(rangeCheck(1,31))
+year = integer.copy().addParseAction(rangeCheck(2000, None))
+
+SLASH = Suppress('/')
+dateExpr = year("year") + SLASH + month("month") + Optional(SLASH + day("day"))
+dateExpr.setName("date")
+
+# convert date fields to datetime (also validates dates as truly valid dates)
+dateExpr.setParseAction(lambda t: datetime(t.year, t.month, t.day or 1).date())
+
+# add range checking on dates
+mindate = datetime(2002,1,1).date()
+maxdate = datetime.now().date()
+dateExpr.addParseAction(rangeCheck(mindate, maxdate))
+
+
+tests = """
+ 2011/5/8
+ 2001/1/1
+ 2004/2/29
+ 2004/2/30
+ 2004/2
+ """.splitlines()
+for t in tests:
+ t = t.strip()
+ if not t: continue
+ print(t)
+ try:
+ print(dateExpr.parseString(t)[0])
+ except Exception as e:
+ print(str(e))
+ print()
+
+
diff --git a/src/examples/shapes.py b/src/examples/shapes.py
new file mode 100644
index 0000000..b5a0ebd
--- /dev/null
+++ b/src/examples/shapes.py
@@ -0,0 +1,64 @@
+# shapes.py
+#
+# A sample program showing how parse actions can convert parsed
+# strings into a data type or object.
+#
+# Copyright 2012, Paul T. McGuire
+#
+
+# define class hierarchy of Shape classes, with polymorphic area method
+class Shape(object):
+ def __init__(self, tokens):
+ self.__dict__.update(tokens.asDict())
+
+ def area(self):
+ raise NotImplementedException()
+
+ def __str__(self):
+ return "<%s>: %s" % (self.__class__.__name__, self.__dict__)
+
+class Square(Shape):
+ def area(self):
+ return self.side**2
+
+class Rectangle(Shape):
+ def area(self):
+ return self.width * self.height
+
+class Circle(Shape):
+ def area(self):
+ return 3.14159 * self.radius**2
+
+
+from pyparsing import *
+
+number = Regex(r'-?\d+(\.\d*)?').setParseAction(lambda t:float(t[0]))
+
+# Shape expressions:
+# square : S <centerx> <centery> <side>
+# rectangle: R <centerx> <centery> <width> <height>
+# circle : C <centerx> <centery> <diameter>
+
+squareDefn = "S" + number('centerx') + number('centery') + number('side')
+rectDefn = "R" + number('centerx') + number('centery') + number('width') + number('height')
+circleDefn = "C" + number('centerx') + number('centery') + number('diameter')
+
+squareDefn.setParseAction(Square)
+rectDefn.setParseAction(Rectangle)
+
+def computeRadius(tokens):
+ tokens['radius'] = tokens.diameter/2.0
+circleDefn.setParseAction(computeRadius, Circle)
+
+shapeExpr = squareDefn | rectDefn | circleDefn
+
+tests = """\
+C 0 0 100
+R 10 10 20 50
+S -1 5 10""".splitlines()
+
+for t in tests:
+ shape = shapeExpr.parseString(t)[0]
+ print(shape)
+ print("Area:", shape.area())
+ print()