From 455b3d02dec8a349646af26cb11666d88c38e541 Mon Sep 17 00:00:00 2001 From: ptmcg Date: Sat, 11 Oct 2014 05:11:47 +0000 Subject: Minor mods to released examples git-svn-id: svn://svn.code.sf.net/p/pyparsing/code/trunk@277 9bf210a0-9d2d-494c-87cf-cfb32e7dff7b --- src/examples/datetimeParseActions.py | 47 ++++++++++++++++++++++ src/examples/parsePythonValue.py | 5 ++- src/examples/position.py | 55 ++++++++++++++++++++++++++ src/examples/rangeCheck.py | 76 ++++++++++++++++++++++++++++++++++++ src/examples/shapes.py | 64 ++++++++++++++++++++++++++++++ 5 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 src/examples/datetimeParseActions.py create mode 100644 src/examples/position.py create mode 100644 src/examples/rangeCheck.py create mode 100644 src/examples/shapes.py (limited to 'src/examples') 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 +# rectangle: R +# circle : C + +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() -- cgit v1.2.1