simpleparse 3.0.0a3__cp313-cp313-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simpleparse/__init__.py +8 -0
- simpleparse/baseparser.py +90 -0
- simpleparse/common/__init__.py +19 -0
- simpleparse/common/calendar_names.py +103 -0
- simpleparse/common/chartypes.py +89 -0
- simpleparse/common/comments.py +72 -0
- simpleparse/common/escapeutils.py +21 -0
- simpleparse/common/iso_date.py +153 -0
- simpleparse/common/iso_date_loose.py +141 -0
- simpleparse/common/numbers.py +166 -0
- simpleparse/common/phonetics.py +76 -0
- simpleparse/common/strings.py +158 -0
- simpleparse/common/timezone_names.py +218 -0
- simpleparse/dispatchprocessor.py +107 -0
- simpleparse/error.py +55 -0
- simpleparse/generator.py +151 -0
- simpleparse/objectgenerator.py +780 -0
- simpleparse/parser.py +49 -0
- simpleparse/printers.py +58 -0
- simpleparse/processor.py +49 -0
- simpleparse/simpleparsegrammar.py +755 -0
- simpleparse/stt/COPYRIGHT +11 -0
- simpleparse/stt/Doc/eGenix-mx-Extensions.html +1708 -0
- simpleparse/stt/Doc/mxLicense.html +868 -0
- simpleparse/stt/Doc/mxTextTools.html +2677 -0
- simpleparse/stt/LICENSE +16 -0
- simpleparse/stt/TextTools/COPYRIGHT +19 -0
- simpleparse/stt/TextTools/Constants/Sets.py +58 -0
- simpleparse/stt/TextTools/Constants/TagTables.py +32 -0
- simpleparse/stt/TextTools/Constants/__init__.py +0 -0
- simpleparse/stt/TextTools/LICENSE +106 -0
- simpleparse/stt/TextTools/TextTools.py +818 -0
- simpleparse/stt/TextTools/__init__.py +63 -0
- simpleparse/stt/TextTools/mxTextTools/__init__.py +23 -0
- simpleparse/stt/TextTools/mxTextTools/highcommands.h +266 -0
- simpleparse/stt/TextTools/mxTextTools/lowlevelcommands.h +814 -0
- simpleparse/stt/TextTools/mxTextTools/mx.h +721 -0
- simpleparse/stt/TextTools/mxTextTools/mxTextTools.c +5364 -0
- simpleparse/stt/TextTools/mxTextTools/mxTextTools.cp313-win32.pyd +0 -0
- simpleparse/stt/TextTools/mxTextTools/mxTextTools.def +2 -0
- simpleparse/stt/TextTools/mxTextTools/mxTextTools.h +327 -0
- simpleparse/stt/TextTools/mxTextTools/mxbm_modern.c +581 -0
- simpleparse/stt/TextTools/mxTextTools/mxbm_modern.h +160 -0
- simpleparse/stt/TextTools/mxTextTools/mxbmse.c +218 -0
- simpleparse/stt/TextTools/mxTextTools/mxbmse.h +65 -0
- simpleparse/stt/TextTools/mxTextTools/mxh.h +60 -0
- simpleparse/stt/TextTools/mxTextTools/mxpyapi.h +35 -0
- simpleparse/stt/TextTools/mxTextTools/mxstdlib.h +225 -0
- simpleparse/stt/TextTools/mxTextTools/mxte.c +47 -0
- simpleparse/stt/TextTools/mxTextTools/mxte_impl.h +878 -0
- simpleparse/stt/TextTools/mxTextTools/mxte_modern.c +383 -0
- simpleparse/stt/TextTools/mxTextTools/mxte_modern.h +806 -0
- simpleparse/stt/TextTools/mxTextTools/mxte_smart.c +113 -0
- simpleparse/stt/TextTools/mxTextTools/recursecommands.h +119 -0
- simpleparse/stt/TextTools/mxTextTools/speccommands.h +101 -0
- simpleparse/stt/__init__.py +22 -0
- simpleparse/stt/mxLicense.html +691 -0
- simpleparse/xmlparser/__init__.py +11 -0
- simpleparse/xmlparser/xml_parser.py +200 -0
- simpleparse-3.0.0a3.dist-info/METADATA +72 -0
- simpleparse-3.0.0a3.dist-info/RECORD +64 -0
- simpleparse-3.0.0a3.dist-info/WHEEL +5 -0
- simpleparse-3.0.0a3.dist-info/licenses/license.txt +31 -0
- simpleparse-3.0.0a3.dist-info/top_level.txt +1 -0
simpleparse/__init__.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Base class for real-world parsers (such as parser.Parser)"""
|
|
2
|
+
|
|
3
|
+
from simpleparse.stt.TextTools.TextTools import tag
|
|
4
|
+
from simpleparse.generator import Generator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BaseParser:
|
|
8
|
+
"""Class on which real-world parsers build
|
|
9
|
+
|
|
10
|
+
Normally you use a sub-class of this class, such as
|
|
11
|
+
simpleparser.parser.Parser
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
_rootProduction = ""
|
|
15
|
+
|
|
16
|
+
# primary API...
|
|
17
|
+
def parse(
|
|
18
|
+
self, data, production=None, processor=None, start=0, stop=None, encoding=None
|
|
19
|
+
):
|
|
20
|
+
"""Parse data with production "production" of this parser
|
|
21
|
+
|
|
22
|
+
data -- data to be parsed, a Python string or bytes
|
|
23
|
+
production -- optional string specifying a non-default production to use
|
|
24
|
+
for parsing data
|
|
25
|
+
processor -- optional pointer to a Processor or MethodSource object for
|
|
26
|
+
use in determining reporting format and/or post-processing the results
|
|
27
|
+
of the parsing pass. Can be None if neither is desired (default)
|
|
28
|
+
start -- starting index for the parsing, default 0
|
|
29
|
+
stop -- stoping index for the parsing, default len(data)
|
|
30
|
+
encoding -- optional encoding for bytes input (e.g., 'utf-8', 'latin-1').
|
|
31
|
+
When specified with bytes input, the grammar patterns are compiled
|
|
32
|
+
to match the encoded byte sequences. Supported encodings:
|
|
33
|
+
- Single-byte: latin-1, iso-8859-*, windows-1252, ascii, etc.
|
|
34
|
+
- Multi-byte: utf-8 only (other multi-byte encodings not supported)
|
|
35
|
+
Positions in the result are byte positions when encoding is used.
|
|
36
|
+
"""
|
|
37
|
+
self.resetBeforeParse()
|
|
38
|
+
if processor is None:
|
|
39
|
+
processor = self.buildProcessor()
|
|
40
|
+
if stop is None:
|
|
41
|
+
stop = len(data)
|
|
42
|
+
value = tag(
|
|
43
|
+
data,
|
|
44
|
+
self.buildTagger(production, processor),
|
|
45
|
+
start,
|
|
46
|
+
stop,
|
|
47
|
+
encoding=encoding,
|
|
48
|
+
)
|
|
49
|
+
if processor and callable(processor):
|
|
50
|
+
return processor(value, data)
|
|
51
|
+
else:
|
|
52
|
+
return value
|
|
53
|
+
|
|
54
|
+
# abstract methods
|
|
55
|
+
def buildProcessor(self):
|
|
56
|
+
"""Build default processor object for this parser class
|
|
57
|
+
|
|
58
|
+
The default implementation returns None. The processor
|
|
59
|
+
can either implement the "method source" API (just provides
|
|
60
|
+
information about Callouts and the like), or the processor
|
|
61
|
+
API and the method-source API. The processor API merely
|
|
62
|
+
requires that the object be callable, and have the signature:
|
|
63
|
+
|
|
64
|
+
object( (success, children, nextPosition), buffer)
|
|
65
|
+
|
|
66
|
+
(Note: your object can treat the first item as a single tuple
|
|
67
|
+
if it likes).
|
|
68
|
+
|
|
69
|
+
See: simpleparse.processor module for details.
|
|
70
|
+
"""
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
def buildTagger(self, name, processor):
|
|
74
|
+
"""Build the tag-table for the parser
|
|
75
|
+
|
|
76
|
+
This method must be implemented by your base class and _not_
|
|
77
|
+
call the implementation here.
|
|
78
|
+
"""
|
|
79
|
+
raise NotImplementedError(
|
|
80
|
+
"""Parser sub-class %s hasn't implemented a buildTagger method"""
|
|
81
|
+
% (self.__class__.__name__)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def resetBeforeParse(self):
|
|
85
|
+
"""Called just before the parser's parse method starts working,
|
|
86
|
+
|
|
87
|
+
Allows you to set up special-purpose structures, such as stacks
|
|
88
|
+
or local storage values. There is no base implementation. The
|
|
89
|
+
base implementation does nothing.
|
|
90
|
+
"""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Common (library) definitions
|
|
2
|
+
|
|
3
|
+
You normally use this module by importing one of our
|
|
4
|
+
sub-modules (which automatically registers itself with
|
|
5
|
+
the SOURCES list defined here).
|
|
6
|
+
|
|
7
|
+
Calling common.share( dictionary ) with a dictionary
|
|
8
|
+
mapping string names to element token instances will
|
|
9
|
+
make the element tokens available under those string
|
|
10
|
+
names in default parsers. Note: a Parser can override
|
|
11
|
+
this by specifying an explicit definitionSources
|
|
12
|
+
parameter in its initialiser.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def share( dictionary ):
|
|
16
|
+
SOURCES.append( dictionary)
|
|
17
|
+
|
|
18
|
+
SOURCES = [
|
|
19
|
+
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Locale-specific calendar names (day-of-week and month-of-year)
|
|
2
|
+
|
|
3
|
+
These values are those returned by the calendar module. Available
|
|
4
|
+
productions:
|
|
5
|
+
|
|
6
|
+
locale_day_names
|
|
7
|
+
locale_day_names_uc
|
|
8
|
+
locale_day_names_lc
|
|
9
|
+
Names for the days of the week
|
|
10
|
+
|
|
11
|
+
locale_day_abbrs
|
|
12
|
+
locale_day_abbrs_uc
|
|
13
|
+
locale_day_abbrs_lc
|
|
14
|
+
Short-forms (3 characters normally) for
|
|
15
|
+
the days of the week.
|
|
16
|
+
|
|
17
|
+
locale_month_names
|
|
18
|
+
locale_month_names_uc
|
|
19
|
+
locale_month_names_lc
|
|
20
|
+
Names for the months of the year
|
|
21
|
+
|
|
22
|
+
locale_month_abbrs
|
|
23
|
+
locale_month_abbrs_uc
|
|
24
|
+
locale_month_abbrs_lc
|
|
25
|
+
Short-forms (3 characters normally) for
|
|
26
|
+
the months of the year
|
|
27
|
+
|
|
28
|
+
Interpreters:
|
|
29
|
+
MonthNameInterpreter
|
|
30
|
+
DayNameInterpreter
|
|
31
|
+
Both offer the ability to set an index other
|
|
32
|
+
than the default (of 1) for the first item in
|
|
33
|
+
the list.
|
|
34
|
+
"""
|
|
35
|
+
import calendar
|
|
36
|
+
from simpleparse import objectgenerator, common
|
|
37
|
+
|
|
38
|
+
c = {}
|
|
39
|
+
|
|
40
|
+
da = calendar.day_abbr[:]
|
|
41
|
+
dn = calendar.day_name[:]
|
|
42
|
+
ma = calendar.month_abbr[:]
|
|
43
|
+
mn = calendar.month_name[:]
|
|
44
|
+
|
|
45
|
+
def _build( name, set ):
|
|
46
|
+
# make sure longest equal-prefix items are first
|
|
47
|
+
set = set[:]
|
|
48
|
+
set.sort()
|
|
49
|
+
set.reverse()
|
|
50
|
+
l,u,r = [],[],[]
|
|
51
|
+
for item in set:
|
|
52
|
+
l.append( objectgenerator.Literal( value = item.lower() ))
|
|
53
|
+
u.append( objectgenerator.Literal( value = item.upper() ))
|
|
54
|
+
r.append( objectgenerator.Literal( value = item ))
|
|
55
|
+
c[ name + '_lc' ] = objectgenerator.FirstOfGroup( children = l )
|
|
56
|
+
c[ name + '_uc' ] = objectgenerator.FirstOfGroup( children = u )
|
|
57
|
+
c[ name ] = objectgenerator.FirstOfGroup( children = r )
|
|
58
|
+
|
|
59
|
+
_build( 'locale_day_names', dn )
|
|
60
|
+
_build( 'locale_day_abbrs', da )
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
_build( 'locale_month_names', mn )
|
|
64
|
+
_build( 'locale_month_abbrs', ma )
|
|
65
|
+
|
|
66
|
+
da = [s.lower() for s in da]
|
|
67
|
+
dn = [s.lower() for s in dn]
|
|
68
|
+
ma = [s.lower() for s in ma]
|
|
69
|
+
mn = [s.lower() for s in mn]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
common.share( c )
|
|
73
|
+
|
|
74
|
+
class NameInterpreter:
|
|
75
|
+
offset = 1
|
|
76
|
+
def __init__( self, offset = 1 ):
|
|
77
|
+
self.offset = offset
|
|
78
|
+
def __call__( self, info, buffer ):
|
|
79
|
+
(tag, left, right, children) = info
|
|
80
|
+
value = buffer[left:right].lower()
|
|
81
|
+
for table in self.tables:
|
|
82
|
+
try:
|
|
83
|
+
return table.index( value )+ self.offset
|
|
84
|
+
except ValueError:
|
|
85
|
+
pass
|
|
86
|
+
raise ValueError( """Unrecognised (but parsed) %s name %s at character %s"""%( self.nameType, value, left))
|
|
87
|
+
|
|
88
|
+
class MonthNameInterpreter( NameInterpreter):
|
|
89
|
+
"""Interpret a month-of-year name as an integer index
|
|
90
|
+
|
|
91
|
+
Pass an "offset" value to __init__ to use an offset other
|
|
92
|
+
than 1 (Monday = 1), normally 0 (Monday = 0)
|
|
93
|
+
"""
|
|
94
|
+
nameType = "Month"
|
|
95
|
+
tables = (mn,ma)
|
|
96
|
+
class DayNameInterpreter( NameInterpreter ):
|
|
97
|
+
"""Interpret a day-of-week name as an integer index
|
|
98
|
+
|
|
99
|
+
Pass an "offset" value to __init__ to use an offset other
|
|
100
|
+
than 1 (January = 1), normally 0 (January = 0)
|
|
101
|
+
"""
|
|
102
|
+
nameType = "Day"
|
|
103
|
+
tables = (dn,da)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Common locale-specific character types
|
|
2
|
+
|
|
3
|
+
Following productions are all based on string module,
|
|
4
|
+
with the default locale specified. The first production
|
|
5
|
+
is a single character of the class and the second a
|
|
6
|
+
repeating character version:
|
|
7
|
+
|
|
8
|
+
digit, digits
|
|
9
|
+
uppercasechar, uppercase
|
|
10
|
+
lowercasechar, lowercase
|
|
11
|
+
letter, letters
|
|
12
|
+
whitespacechar, whitespace
|
|
13
|
+
punctuationchar, punctuation
|
|
14
|
+
octdigit, octdigits
|
|
15
|
+
hexdigit, hexdigits
|
|
16
|
+
printablechar, printable
|
|
17
|
+
|
|
18
|
+
For Python versions with the constants in the string module:
|
|
19
|
+
ascii_letter, ascii_letters
|
|
20
|
+
ascii_lowercasechar, ascii_lowercase
|
|
21
|
+
ascii_uppercasechar, ascii_uppercase
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Following are locale-specific values, both are
|
|
25
|
+
single-character values:
|
|
26
|
+
|
|
27
|
+
locale_decimal_point -- locale-specific decimal seperator
|
|
28
|
+
locale_thousands_seperator -- locale-specific "thousands" seperator
|
|
29
|
+
|
|
30
|
+
Others:
|
|
31
|
+
|
|
32
|
+
EOF -- Matches iff parsing has reached the end of the buffer
|
|
33
|
+
|
|
34
|
+
There are no interpreters provided (the types are considered
|
|
35
|
+
too common to provide meaningful interpreters).
|
|
36
|
+
"""
|
|
37
|
+
from simpleparse import objectgenerator, common
|
|
38
|
+
import string, locale
|
|
39
|
+
try:
|
|
40
|
+
locale.setlocale(locale.LC_ALL, "" )
|
|
41
|
+
except locale.Error:
|
|
42
|
+
# Environment requests a locale that isn't installed (common in minimal
|
|
43
|
+
# containers). localeconv() still works against the default C locale, so
|
|
44
|
+
# the only consequence is C-locale decimal/thousands conventions below.
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
c = {}
|
|
48
|
+
|
|
49
|
+
# string-module items...
|
|
50
|
+
|
|
51
|
+
for source,single,repeat in [
|
|
52
|
+
("digits","digit","digits"),
|
|
53
|
+
("ascii_uppercase", "uppercasechar", "uppercase"),
|
|
54
|
+
("ascii_lowercase", "lowercasechar", "lowercase"),
|
|
55
|
+
("ascii_letters", "letter", "letters" ),
|
|
56
|
+
("ascii_letters", "ascii_letter", "ascii_letters" ), # alias
|
|
57
|
+
("ascii_lowercase", "ascii_lowercasechar", "ascii_lowercase"),
|
|
58
|
+
("ascii_uppercase", "ascii_uppercasechar", "ascii_uppercase"),
|
|
59
|
+
("whitespace", "whitespacechar", "whitespace"),
|
|
60
|
+
("punctuation", "punctuationchar", "punctuation"),
|
|
61
|
+
("octdigits", "octdigit", "octdigits"),
|
|
62
|
+
("hexdigits", "hexdigit", "hexdigits"),
|
|
63
|
+
("printable", "printablechar", "printable"),
|
|
64
|
+
]:
|
|
65
|
+
try:
|
|
66
|
+
value = getattr( string, source )
|
|
67
|
+
c[ single ] = objectgenerator.Range( value = value )
|
|
68
|
+
c[ repeat ] = objectgenerator.Range( value = value, repeating =1 )
|
|
69
|
+
except AttributeError:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
# locale-module items
|
|
73
|
+
_lc = locale.localeconv()
|
|
74
|
+
c[ "locale_decimal_point" ] = objectgenerator.Literal( value = _lc["decimal_point"] )
|
|
75
|
+
c[ "locale_thousands_seperator" ] = objectgenerator.Literal( value = _lc["thousands_sep"] )
|
|
76
|
+
|
|
77
|
+
del _lc
|
|
78
|
+
|
|
79
|
+
# common, but not really well defined sets
|
|
80
|
+
# this is the set of characters which are interpreted
|
|
81
|
+
# specially by Python's string-escaping when they
|
|
82
|
+
# follow a \\ char.
|
|
83
|
+
|
|
84
|
+
from simpleparse.stt import TextTools
|
|
85
|
+
c[ "EOF" ] = objectgenerator.Prebuilt( value = (
|
|
86
|
+
(None, TextTools.EOF, TextTools.Here),
|
|
87
|
+
) )
|
|
88
|
+
|
|
89
|
+
common.share( c )
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Common comment formats
|
|
2
|
+
|
|
3
|
+
To process, handle the "comment" production,
|
|
4
|
+
(the specific named comment formats are all
|
|
5
|
+
expanded productions, so you won't get them
|
|
6
|
+
returned for processing).
|
|
7
|
+
|
|
8
|
+
hash_comment
|
|
9
|
+
# to EOL comments
|
|
10
|
+
slashslash_comment
|
|
11
|
+
// to EOL comments
|
|
12
|
+
semicolon_comment
|
|
13
|
+
; to EOL comments
|
|
14
|
+
slashbang_comment
|
|
15
|
+
c_comment
|
|
16
|
+
non-nesting /* */ comments
|
|
17
|
+
slashbang_nest_comment
|
|
18
|
+
c_nest_comment
|
|
19
|
+
nesting /* /* */ */ comments
|
|
20
|
+
"""
|
|
21
|
+
from simpleparse.parser import Parser
|
|
22
|
+
from simpleparse import common, objectgenerator
|
|
23
|
+
from simpleparse.common import chartypes
|
|
24
|
+
|
|
25
|
+
c = {}
|
|
26
|
+
|
|
27
|
+
eolcomments = r"""
|
|
28
|
+
### comment formats where the comment goes
|
|
29
|
+
### from a marker to the end of the line
|
|
30
|
+
|
|
31
|
+
comment := -'\012'*
|
|
32
|
+
<EOL> := ('\r'?,'\n')/EOF
|
|
33
|
+
|
|
34
|
+
>hash_comment< := '#', comment, EOL
|
|
35
|
+
>semicolon_comment< := ';', comment, EOL
|
|
36
|
+
>slashslash_comment< := '//', comment, EOL
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
_p = Parser( eolcomments )
|
|
40
|
+
for name in ["hash_comment", "semicolon_comment", "slashslash_comment"]:
|
|
41
|
+
c[ name ] = objectgenerator.LibraryElement(
|
|
42
|
+
generator = _p._generator,
|
|
43
|
+
production = name,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
ccomments = r"""
|
|
47
|
+
### comments in format /* comment */ with no recursion allowed
|
|
48
|
+
comment := -"*/"*
|
|
49
|
+
>slashbang_comment< := '/*', comment, '*/'
|
|
50
|
+
"""
|
|
51
|
+
_p = Parser( ccomments )
|
|
52
|
+
for name in ["c_comment","slashbang_comment"]:
|
|
53
|
+
c[ name ] = objectgenerator.LibraryElement(
|
|
54
|
+
generator = _p._generator,
|
|
55
|
+
production = "slashbang_comment",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
nccomments = r"""
|
|
59
|
+
### nestable C comments of form /* comment /* innercomment */ back to previous */
|
|
60
|
+
<comment_start> := '/*'
|
|
61
|
+
<comment_stop> := '*/'
|
|
62
|
+
comment := (-(comment_stop/comment_start)+/slashbang_nest_comment)*
|
|
63
|
+
>slashbang_nest_comment< := comment_start, comment, comment_stop
|
|
64
|
+
"""
|
|
65
|
+
_p = Parser( nccomments )
|
|
66
|
+
for name in ["c_nest_comment","slashbang_nest_comment"]:
|
|
67
|
+
c[ name ] = objectgenerator.LibraryElement(
|
|
68
|
+
generator = _p._generator,
|
|
69
|
+
production = "slashbang_nest_comment",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
common.share(c)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Shared escape sequence utilities for SimpleParse
|
|
2
|
+
|
|
3
|
+
This module provides common escape sequence mappings used by both
|
|
4
|
+
the grammar parser and string literal processing.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Map of escape sequences to their corresponding characters
|
|
8
|
+
# Used for interpreting backslash escapes in string literals and grammar definitions
|
|
9
|
+
SPECIAL_ESCAPED_MAP = {
|
|
10
|
+
'a': '\a', # Bell
|
|
11
|
+
'b': '\b', # Backspace
|
|
12
|
+
'f': '\f', # Form feed
|
|
13
|
+
'n': '\n', # Newline
|
|
14
|
+
'r': '\r', # Carriage return
|
|
15
|
+
't': '\t', # Tab
|
|
16
|
+
'v': '\v', # Vertical tab
|
|
17
|
+
'\\': '\\', # Backslash
|
|
18
|
+
'\n': '', # Escaped newline (line continuation)
|
|
19
|
+
'"': '"', # Double quote
|
|
20
|
+
"'": "'", # Single quote
|
|
21
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Canonical ISO date format YYYY-MM-DDTHH:mm:SS+HH:mm
|
|
2
|
+
|
|
3
|
+
This parser is _extremely_ strict, and the dates that match it,
|
|
4
|
+
though really easy to work with for the computer, are not particularly
|
|
5
|
+
readable. See the iso_date_loose module for a slightly relaxed
|
|
6
|
+
definition which allows the "T" character to be replaced by a
|
|
7
|
+
" " character, and allows a space before the timezone offset, as well
|
|
8
|
+
as allowing the integer values to use non-0-padded integers.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
ISO_date -- YYYY-MM-DD format, with a month and date optional
|
|
12
|
+
ISO_time -- HH:mm:SS format, with minutes and seconds optional
|
|
13
|
+
ISO_date_time -- YYYY-MM-DD HH:mm:SS+HH:mm format,
|
|
14
|
+
with time optional and TimeZone offset optional
|
|
15
|
+
|
|
16
|
+
Interpreter:
|
|
17
|
+
MxInterpreter
|
|
18
|
+
Interprets the parse tree as mx.DateTime values
|
|
19
|
+
ISO_date and ISO_time
|
|
20
|
+
returns DateTime objects
|
|
21
|
+
Time only
|
|
22
|
+
returns RelativeDateTime object which, when
|
|
23
|
+
added to a DateTime gives you the given time
|
|
24
|
+
within that day
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
from mx import DateTime
|
|
28
|
+
haveMX = 1
|
|
29
|
+
except ImportError:
|
|
30
|
+
haveMX = 0
|
|
31
|
+
from simpleparse.parser import Parser
|
|
32
|
+
from simpleparse import common, objectgenerator
|
|
33
|
+
from simpleparse.common import chartypes, numbers
|
|
34
|
+
from simpleparse.dispatchprocessor import *
|
|
35
|
+
|
|
36
|
+
c = {}
|
|
37
|
+
|
|
38
|
+
declaration ="""
|
|
39
|
+
year := digit,digit,digit,digit
|
|
40
|
+
month := digit,digit
|
|
41
|
+
day := digit,digit
|
|
42
|
+
|
|
43
|
+
hour := digit,digit
|
|
44
|
+
minute := digit,digit
|
|
45
|
+
second := digit,digit
|
|
46
|
+
offset_sign := [-+]
|
|
47
|
+
offset := offset_sign, hour, time_separator?, minute
|
|
48
|
+
|
|
49
|
+
<date_separator> := '-'
|
|
50
|
+
<time_separator> := ':'
|
|
51
|
+
|
|
52
|
+
ISO_date := year, (date_separator, month, (date_separator, day)?)?
|
|
53
|
+
ISO_time := hour, (time_separator, minute, (time_separator, second)?)?
|
|
54
|
+
ISO_date_time := ISO_date, ([T], ISO_time)?, offset?
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
_p = Parser( declaration )
|
|
61
|
+
for name in ["ISO_time","ISO_date", "ISO_date_time"]:
|
|
62
|
+
c[ name ] = objectgenerator.LibraryElement(
|
|
63
|
+
generator = _p._generator,
|
|
64
|
+
production = name,
|
|
65
|
+
)
|
|
66
|
+
common.share( c )
|
|
67
|
+
|
|
68
|
+
if haveMX:
|
|
69
|
+
class MxInterpreter(DispatchProcessor):
|
|
70
|
+
"""Interpret a parsed ISO_date_time_loose in GMT/UTC time or localtime
|
|
71
|
+
"""
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
inputLocal = 1,
|
|
75
|
+
returnLocal = 1,
|
|
76
|
+
):
|
|
77
|
+
self.inputLocal = inputLocal
|
|
78
|
+
self.returnLocal = returnLocal
|
|
79
|
+
dateName = 'ISO_date'
|
|
80
|
+
timeName = 'ISO_time'
|
|
81
|
+
def ISO_date_time( self, info, buffer):
|
|
82
|
+
"""Interpret the loose ISO date + time format"""
|
|
83
|
+
(tag, left, right, sublist) = info
|
|
84
|
+
set = singleMap( sublist, self, buffer )
|
|
85
|
+
base, time, offset = (
|
|
86
|
+
set.get(self.dateName),
|
|
87
|
+
set.get(self.timeName) or DateTime.RelativeDateTime(hour=0,minute=0,second=0),
|
|
88
|
+
set.get( "offset" ),
|
|
89
|
+
)
|
|
90
|
+
base = base + time
|
|
91
|
+
offset = set.get( "offset" )
|
|
92
|
+
if offset is not None:
|
|
93
|
+
# an explicit timezone was entered, convert to gmt and return as appropriate...
|
|
94
|
+
gmt = base - offset
|
|
95
|
+
if self.returnLocal:
|
|
96
|
+
return gmt.localtime()
|
|
97
|
+
else:
|
|
98
|
+
return gmt
|
|
99
|
+
# was in the default input locale (either gmt or local)
|
|
100
|
+
if self.inputLocal and self.returnLocal:
|
|
101
|
+
return base
|
|
102
|
+
elif not self.inputLocal and not self.returnLocal:
|
|
103
|
+
return base
|
|
104
|
+
elif self.inputLocal and not self.returnLocal:
|
|
105
|
+
# return gmt from local...
|
|
106
|
+
return base.gmtime()
|
|
107
|
+
else:
|
|
108
|
+
return base.localtime()
|
|
109
|
+
def ISO_date( self, info, buffer):
|
|
110
|
+
"""Interpret the ISO date format"""
|
|
111
|
+
(tag, left, right, sublist) = info
|
|
112
|
+
set = {}
|
|
113
|
+
for item in sublist:
|
|
114
|
+
set[ item[0] ] = dispatch( self, item, buffer)
|
|
115
|
+
return DateTime.DateTime(
|
|
116
|
+
set.get("year") or now().year,
|
|
117
|
+
set.get("month") or 1,
|
|
118
|
+
set.get("day") or 1,
|
|
119
|
+
)
|
|
120
|
+
def ISO_time( self, info, buffer):
|
|
121
|
+
"""Interpret the ISO time format"""
|
|
122
|
+
(tag, left, right, sublist) = info
|
|
123
|
+
set = {}
|
|
124
|
+
for item in sublist:
|
|
125
|
+
set[ item[0] ] = dispatch( self, item, buffer)
|
|
126
|
+
return DateTime.RelativeDateTime(
|
|
127
|
+
hour = set.get("hour") or 0,
|
|
128
|
+
minute = set.get("minute") or 0,
|
|
129
|
+
second = set.get("second") or 0,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
integer = numbers.IntInterpreter()
|
|
133
|
+
second = offset_minute = offset_hour = year = month = day = hour =minute =integer
|
|
134
|
+
|
|
135
|
+
def offset( self, info, buffer):
|
|
136
|
+
"""Calculate the time zone offset as a date-time delta"""
|
|
137
|
+
(tag, left, right, sublist) = info
|
|
138
|
+
set = singleMap( sublist, self, buffer )
|
|
139
|
+
direction = set.get('offset_sign',1)
|
|
140
|
+
hour = set.get( "hour", 0)
|
|
141
|
+
minute = set.get( "minute", 0)
|
|
142
|
+
delta = DateTime.DateTimeDelta( 0, hour*direction, minute*direction)
|
|
143
|
+
return delta
|
|
144
|
+
|
|
145
|
+
def offset_sign( self , info, buffer):
|
|
146
|
+
"""Interpret the offset sign as a multiplier"""
|
|
147
|
+
(tag, left, right, sublist) = info
|
|
148
|
+
v = buffer [left: right]
|
|
149
|
+
if v in ' +':
|
|
150
|
+
return 1
|
|
151
|
+
else:
|
|
152
|
+
return -1
|
|
153
|
+
|