Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pystache_custom
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
pystache_custom
Commits
854b84c9
Commit
854b84c9
authored
Jan 14, 2012
by
Chris Jerdonek
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Created a parser module.
parent
19ac543d
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
199 additions
and
183 deletions
+199
-183
pystache/parser.py
+182
-0
pystache/renderengine.py
+17
-183
No files found.
pystache/parser.py
0 → 100644
View file @
854b84c9
# coding: utf-8
"""
Provides a class for parsing template strings.
This module is only meant for internal use by the renderengine module.
"""
import
re
DEFAULT_DELIMITERS
=
(
'{{'
,
'}}'
)
END_OF_LINE_CHARACTERS
=
[
'
\r
'
,
'
\n
'
]
def
_compile_template_re
(
delimiters
):
# The possible tag type characters following the opening tag,
# excluding "=" and "{".
tag_types
=
"!>&/#^"
# TODO: are we following this in the spec?
#
# The tag's content MUST be a non-whitespace character sequence
# NOT containing the current closing delimiter.
#
tag
=
r"""
(?P<whitespace>[\ \t]*)
%(otag)
s \s*
(?:
(?P<change>=) \s* (?P<delims>.+?) \s* = |
(?P<raw>{) \s* (?P<raw_name>.+?) \s* } |
(?P<tag>[
%(tag_types)
s]?) \s* (?P<name>[\s\S]+?)
)
\s*
%(ctag)
s
"""
%
{
'tag_types'
:
tag_types
,
'otag'
:
re
.
escape
(
delimiters
[
0
]),
'ctag'
:
re
.
escape
(
delimiters
[
1
])}
return
re
.
compile
(
tag
,
re
.
VERBOSE
)
class
EndOfSection
(
Exception
):
def
__init__
(
self
,
parse_tree
,
template
,
position
):
self
.
parse_tree
=
parse_tree
self
.
template
=
template
self
.
position
=
position
class
Parser
(
object
):
_delimiters
=
None
_template_re
=
None
def
__init__
(
self
,
engine
,
delimiters
=
None
):
"""
Construct an instance.
Arguments:
engine: a RenderEngine instance.
"""
if
delimiters
is
None
:
delimiters
=
DEFAULT_DELIMITERS
self
.
_delimiters
=
delimiters
self
.
engine
=
engine
def
compile_template_re
(
self
):
self
.
_template_re
=
_compile_template_re
(
self
.
_delimiters
)
def
_change_delimiters
(
self
,
delimiters
):
self
.
_delimiters
=
delimiters
self
.
compile_template_re
()
def
parse
(
self
,
template
,
index
=
0
):
"""
Parse a template string into a syntax tree using current attributes.
This method uses the current RenderEngine instance's attributes,
including the current tag delimiter, etc.
"""
parse_tree
=
[]
start_index
=
index
while
True
:
match
=
self
.
_template_re
.
search
(
template
,
index
)
if
match
is
None
:
break
match_index
=
match
.
start
()
end_index
=
match
.
end
()
before_tag
=
template
[
index
:
match_index
]
parse_tree
.
append
(
before_tag
)
matches
=
match
.
groupdict
()
index
=
self
.
_handle_match
(
template
,
parse_tree
,
matches
,
start_index
,
match_index
,
end_index
)
# Save the rest of the template.
parse_tree
.
append
(
template
[
index
:])
return
parse_tree
def
_handle_match
(
self
,
template
,
parse_tree
,
matches
,
start_index
,
match_index
,
end_index
):
engine
=
self
.
engine
# Normalize the matches dictionary.
if
matches
[
'change'
]
is
not
None
:
matches
.
update
(
tag
=
'='
,
name
=
matches
[
'delims'
])
elif
matches
[
'raw'
]
is
not
None
:
matches
.
update
(
tag
=
'&'
,
name
=
matches
[
'raw_name'
])
tag_type
=
matches
[
'tag'
]
# Standalone (non-interpolation) tags consume the entire line,
# both leading whitespace and trailing newline.
did_tag_begin_line
=
match_index
==
0
or
template
[
match_index
-
1
]
in
END_OF_LINE_CHARACTERS
did_tag_end_line
=
end_index
==
len
(
template
)
or
template
[
end_index
]
in
END_OF_LINE_CHARACTERS
is_tag_interpolating
=
tag_type
in
[
''
,
'&'
]
if
did_tag_begin_line
and
did_tag_end_line
and
not
is_tag_interpolating
:
if
end_index
<
len
(
template
):
end_index
+=
template
[
end_index
]
==
'
\r
'
and
1
or
0
if
end_index
<
len
(
template
):
end_index
+=
template
[
end_index
]
==
'
\n
'
and
1
or
0
elif
matches
[
'whitespace'
]:
parse_tree
.
append
(
matches
[
'whitespace'
])
match_index
+=
len
(
matches
[
'whitespace'
])
matches
[
'whitespace'
]
=
''
name
=
matches
[
'name'
]
if
tag_type
==
'!'
:
return
end_index
if
tag_type
==
'='
:
delimiters
=
name
.
split
()
self
.
_change_delimiters
(
delimiters
)
return
end_index
if
tag_type
==
'>'
:
func
=
engine
.
_make_get_partial
(
name
,
matches
[
'whitespace'
])
elif
tag_type
in
[
'#'
,
'^'
]:
try
:
self
.
parse
(
template
=
template
,
index
=
end_index
)
except
EndOfSection
as
e
:
bufr
=
e
.
parse_tree
tmpl
=
e
.
template
end_index
=
e
.
position
if
tag_type
==
'#'
:
func
=
engine
.
_make_get_section
(
name
,
bufr
,
tmpl
,
self
.
_delimiters
)
else
:
func
=
engine
.
_make_get_inverse
(
name
,
bufr
)
elif
tag_type
==
'&'
:
func
=
engine
.
_make_get_literal
(
name
)
elif
tag_type
==
''
:
func
=
engine
.
_make_get_escaped
(
name
)
elif
tag_type
==
'/'
:
# TODO: don't use exceptions for flow control.
raise
EndOfSection
(
parse_tree
,
template
[
start_index
:
match_index
],
end_index
)
else
:
raise
Exception
(
"Unrecognized tag type:
%
s"
%
repr
(
tag_type
))
parse_tree
.
append
(
func
)
return
end_index
pystache/renderengine.py
View file @
854b84c9
...
...
@@ -7,34 +7,10 @@ Defines a class responsible for rendering logic.
import
re
from
parser
import
Parser
DEFAULT_DELIMITERS
=
(
'{{'
,
'}}'
)
END_OF_LINE_CHARACTERS
=
[
'
\r
'
,
'
\n
'
]
def
_compile_template_re
(
delimiters
):
# The possible tag type characters following the opening tag,
# excluding "=" and "{".
tag_types
=
"!>&/#^"
# TODO: are we following this in the spec?
#
# The tag's content MUST be a non-whitespace character sequence
# NOT containing the current closing delimiter.
#
tag
=
r"""
(?P<whitespace>[\ \t]*)
%(otag)
s \s*
(?:
(?P<change>=) \s* (?P<delims>.+?) \s* = |
(?P<raw>{) \s* (?P<raw_name>.+?) \s* } |
(?P<tag>[
%(tag_types)
s]?) \s* (?P<name>[\s\S]+?)
)
\s*
%(ctag)
s
"""
%
{
'tag_types'
:
tag_types
,
'otag'
:
re
.
escape
(
delimiters
[
0
]),
'ctag'
:
re
.
escape
(
delimiters
[
1
])}
return
re
.
compile
(
tag
,
re
.
VERBOSE
)
NON_BLANK_RE
=
re
.
compile
(
r'^(.)'
,
re
.
M
)
def
render_parse_tree
(
parse_tree
,
context
):
...
...
@@ -70,27 +46,6 @@ def render_parse_tree(parse_tree, context):
return
unicode
(
s
)
def
_make_get_inverse
(
name
,
parsed
):
def
get_inverse
(
context
):
"""
Returns a string with type unicode.
"""
data
=
context
.
get
(
name
)
if
data
:
return
u''
return
render_parse_tree
(
parsed
,
context
)
return
get_inverse
class
EndOfSection
(
Exception
):
def
__init__
(
self
,
parse_tree
,
template
,
position
):
self
.
parse_tree
=
parse_tree
self
.
template
=
template
self
.
position
=
position
class
RenderEngine
(
object
):
"""
...
...
@@ -110,8 +65,6 @@ class RenderEngine(object):
"""
nonblank_re
=
re
.
compile
(
r'^(.)'
,
re
.
M
)
def
__init__
(
self
,
load_partial
=
None
,
literal
=
None
,
escape
=
None
):
"""
Arguments:
...
...
@@ -217,11 +170,24 @@ class RenderEngine(object):
"""
template
=
self
.
load_partial
(
name
)
# Indent before rendering.
template
=
re
.
sub
(
self
.
nonblank_re
,
indentation
+
r'\1'
,
template
)
template
=
re
.
sub
(
NON_BLANK_RE
,
indentation
+
r'\1'
,
template
)
return
self
.
_render
(
template
,
context
)
return
get_partial
def
_make_get_inverse
(
self
,
name
,
parsed
):
def
get_inverse
(
context
):
"""
Returns a string with type unicode.
"""
data
=
context
.
get
(
name
)
if
data
:
return
u''
return
render_parse_tree
(
parsed
,
context
)
return
get_inverse
def
_make_get_section
(
self
,
name
,
parse_tree_
,
template_
,
delims
):
def
get_section
(
context
):
"""
...
...
@@ -256,7 +222,7 @@ class RenderEngine(object):
Parse the given template into a parse tree using a new parser.
"""
parser
=
_
Parser
(
self
,
delimiters
=
delimiters
)
parser
=
Parser
(
self
,
delimiters
=
delimiters
)
parser
.
compile_template_re
()
return
parser
.
parse
(
template
=
template_string
)
...
...
@@ -296,135 +262,3 @@ class RenderEngine(object):
template
=
unicode
(
template
)
return
self
.
_render
(
template
,
context
)
class
_Parser
(
object
):
_delimiters
=
None
_template_re
=
None
def
__init__
(
self
,
engine
,
delimiters
=
None
):
"""
Construct an instance.
"""
if
delimiters
is
None
:
delimiters
=
DEFAULT_DELIMITERS
self
.
_delimiters
=
delimiters
self
.
engine
=
engine
def
compile_template_re
(
self
):
self
.
_template_re
=
_compile_template_re
(
self
.
_delimiters
)
def
_change_delimiters
(
self
,
delimiters
):
self
.
_delimiters
=
delimiters
self
.
compile_template_re
()
def
parse
(
self
,
template
,
index
=
0
):
"""
Parse a template string into a syntax tree using current attributes.
This method uses the current RenderEngine instance's attributes,
including the current tag delimiter, etc.
"""
parse_tree
=
[]
start_index
=
index
while
True
:
match
=
self
.
_template_re
.
search
(
template
,
index
)
if
match
is
None
:
break
match_index
=
match
.
start
()
end_index
=
match
.
end
()
before_tag
=
template
[
index
:
match_index
]
parse_tree
.
append
(
before_tag
)
matches
=
match
.
groupdict
()
index
=
self
.
_handle_match
(
template
,
parse_tree
,
matches
,
start_index
,
match_index
,
end_index
)
# Save the rest of the template.
parse_tree
.
append
(
template
[
index
:])
return
parse_tree
def
_handle_match
(
self
,
template
,
parse_tree
,
matches
,
start_index
,
match_index
,
end_index
):
engine
=
self
.
engine
# Normalize the matches dictionary.
if
matches
[
'change'
]
is
not
None
:
matches
.
update
(
tag
=
'='
,
name
=
matches
[
'delims'
])
elif
matches
[
'raw'
]
is
not
None
:
matches
.
update
(
tag
=
'&'
,
name
=
matches
[
'raw_name'
])
tag_type
=
matches
[
'tag'
]
# Standalone (non-interpolation) tags consume the entire line,
# both leading whitespace and trailing newline.
did_tag_begin_line
=
match_index
==
0
or
template
[
match_index
-
1
]
in
END_OF_LINE_CHARACTERS
did_tag_end_line
=
end_index
==
len
(
template
)
or
template
[
end_index
]
in
END_OF_LINE_CHARACTERS
is_tag_interpolating
=
tag_type
in
[
''
,
'&'
]
if
did_tag_begin_line
and
did_tag_end_line
and
not
is_tag_interpolating
:
if
end_index
<
len
(
template
):
end_index
+=
template
[
end_index
]
==
'
\r
'
and
1
or
0
if
end_index
<
len
(
template
):
end_index
+=
template
[
end_index
]
==
'
\n
'
and
1
or
0
elif
matches
[
'whitespace'
]:
parse_tree
.
append
(
matches
[
'whitespace'
])
match_index
+=
len
(
matches
[
'whitespace'
])
matches
[
'whitespace'
]
=
''
name
=
matches
[
'name'
]
if
tag_type
==
'!'
:
return
end_index
if
tag_type
==
'='
:
delimiters
=
name
.
split
()
self
.
_change_delimiters
(
delimiters
)
return
end_index
if
tag_type
==
'>'
:
func
=
engine
.
_make_get_partial
(
name
,
matches
[
'whitespace'
])
elif
tag_type
in
[
'#'
,
'^'
]:
try
:
self
.
parse
(
template
=
template
,
index
=
end_index
)
except
EndOfSection
as
e
:
bufr
=
e
.
parse_tree
tmpl
=
e
.
template
end_index
=
e
.
position
if
tag_type
==
'#'
:
func
=
engine
.
_make_get_section
(
name
,
bufr
,
tmpl
,
self
.
_delimiters
)
else
:
func
=
_make_get_inverse
(
name
,
bufr
)
elif
tag_type
==
'&'
:
func
=
engine
.
_make_get_literal
(
name
)
elif
tag_type
==
''
:
func
=
engine
.
_make_get_escaped
(
name
)
elif
tag_type
==
'/'
:
# TODO: don't use exceptions for flow control.
raise
EndOfSection
(
parse_tree
,
template
[
start_index
:
match_index
],
end_index
)
else
:
raise
Exception
(
"Unrecognized tag type:
%
s"
%
repr
(
tag_type
))
parse_tree
.
append
(
func
)
return
end_index
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment