[Populate] Added pybars and pymeta
This commit is contained in:
parent
f3049351da
commit
8512b41feb
|
|
@ -1,4 +1,3 @@
|
|||
#!/usr/bin/env python3
|
||||
# Author: Jan Mrázek
|
||||
# License: MIT
|
||||
|
||||
|
|
@ -21,7 +20,7 @@ try:
|
|||
except ModuleNotFoundError:
|
||||
InlineParser = mistune.InlineLexer
|
||||
HTMLRenderer = mistune.Renderer
|
||||
import pybars # type: ignore
|
||||
from . import pybars # type: ignore
|
||||
import yaml
|
||||
|
||||
from . import mdrenderer
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
#
|
||||
# Copyright (c) 2012, Canonical Ltd
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, version 3 only.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# GNU Lesser General Public License version 3 (see the file LICENSE).
|
||||
|
||||
|
||||
# same format as sys.version_info: "A tuple containing the five components of
|
||||
# the version number: major, minor, micro, releaselevel, and serial. All
|
||||
# values except releaselevel are integers; the release level is 'alpha',
|
||||
# 'beta', 'candidate', or 'final'. The version_info value corresponding to the
|
||||
# Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a
|
||||
# releaselevel of 'dev' for unreleased under-development code.
|
||||
#
|
||||
# If the releaselevel is 'alpha' then the major/minor/micro components are not
|
||||
# established at this point, and setup.py will use a version of next-$(revno).
|
||||
# If the releaselevel is 'final', then the tarball will be major.minor.micro.
|
||||
# Otherwise it is major.minor.micro~$(revno).
|
||||
|
||||
from ._compiler import (
|
||||
Compiler,
|
||||
strlist,
|
||||
Scope,
|
||||
PybarsError
|
||||
)
|
||||
|
||||
__version__ = '0.9.7'
|
||||
__version_info__ = (0, 9, 7, 'final', 0)
|
||||
|
||||
__all__ = [
|
||||
'Compiler',
|
||||
'log',
|
||||
'strlist',
|
||||
'Scope',
|
||||
'PybarsError'
|
||||
]
|
||||
|
||||
|
||||
def log(value):
|
||||
return None
|
||||
|
|
@ -0,0 +1,943 @@
|
|||
#
|
||||
# Copyright (c) 2015 Will Bond, Mjumbe Wawatu Ukweli, 2012 Canonical Ltd
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, version 3 only.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# GNU Lesser General Public License version 3 (see the file LICENSE).
|
||||
|
||||
"""The compiler for pybars."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from types import ModuleType
|
||||
import linecache
|
||||
|
||||
# The following code allows importing pybars from the current dir
|
||||
prev_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if sys.path[0] != prev_dir:
|
||||
try:
|
||||
sys.path.remove(prev_dir)
|
||||
except ValueError:
|
||||
pass
|
||||
sys.path.insert(0, prev_dir)
|
||||
|
||||
import pybars
|
||||
from pybars import _templates
|
||||
from .pymeta.grammar import OMeta
|
||||
|
||||
__all__ = [
|
||||
'Compiler',
|
||||
'strlist',
|
||||
'Scope'
|
||||
]
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
# This allows the code to run on Python 2 and 3 by
|
||||
# creating a consistent reference for the appropriate
|
||||
# string class
|
||||
try:
|
||||
str_class = unicode
|
||||
except NameError:
|
||||
# Python 3 support
|
||||
str_class = str
|
||||
|
||||
# backward compatibility for basestring for python < 2.3
|
||||
try:
|
||||
basestring
|
||||
except NameError:
|
||||
basestring = str
|
||||
|
||||
# Flag for testing
|
||||
debug = False
|
||||
|
||||
|
||||
# Note that unless we presume handlebars is only generating valid html, we have
|
||||
# to accept anything - so a broken template won't be all that visible - it will
|
||||
# just render literally (because the anything rule matches it).
|
||||
|
||||
# this grammar generates a tokenised tree
|
||||
handlebars_grammar = r"""
|
||||
|
||||
template ::= (<text> | <templatecommand>)*:body => ['template'] + body
|
||||
text ::= <newline_text> | <whitespace_text> | <other_text>
|
||||
newline_text ::= (~(<start>) ('\r'?'\n'):char) => ('newline', u'' + char)
|
||||
whitespace_text ::= (~(<start>) (' '|'\t'))+:text => ('whitespace', u''.join(text))
|
||||
other_text ::= (~(<start>) <anything>)+:text => ('literal', u''.join(text))
|
||||
other ::= <anything>:char => ('literal', u'' + char)
|
||||
templatecommand ::= <blockrule>
|
||||
| <comment>
|
||||
| <escapedexpression>
|
||||
| <expression>
|
||||
| <partial>
|
||||
| <rawblock>
|
||||
start ::= '{' '{'
|
||||
finish ::= '}' '}'
|
||||
comment ::= <start> '!' (~(<finish>) <anything>)* <finish> => ('comment', )
|
||||
space ::= ' '|'\t'|'\r'|'\n'
|
||||
arguments ::= (<space>+ (<kwliteral>|<literal>|<path>|<subexpression>))*:arguments => arguments
|
||||
subexpression ::= '(' <spaces> <path>:p (<space>+ (<kwliteral>|<literal>|<path>|<subexpression>))*:arguments <spaces> ')' => ('subexpr', p, arguments)
|
||||
expression_inner ::= <spaces> <path>:p <arguments>:arguments <spaces> <finish> => (p, arguments)
|
||||
expression ::= <start> '{' <expression_inner>:e '}' => ('expand', ) + e
|
||||
| <start> '&' <expression_inner>:e => ('expand', ) + e
|
||||
escapedexpression ::= <start> <expression_inner>:e => ('escapedexpand', ) + e
|
||||
block_inner ::= <spaces> <symbol>:s <arguments>:args <spaces> <finish>
|
||||
=> (u''.join(s), args)
|
||||
partial_inner ::= <spaces> <partialname>:s <arguments>:args <spaces> <finish>
|
||||
=> (s, args)
|
||||
alt_inner ::= <spaces> ('^' | 'e' 'l' 's' 'e') <spaces> <finish>
|
||||
partial ::= <start> '>' <partial_inner>:i => ('partial',) + i
|
||||
path ::= ~('/') <pathseg>+:segments => ('path', segments)
|
||||
kwliteral ::= <safesymbol>:s '=' (<literal>|<path>|<subexpression>):v => ('kwparam', s, v)
|
||||
literal ::= (<string>|<integer>|<boolean>|<null>|<undefined>):thing => ('literalparam', thing)
|
||||
string ::= '"' <notdquote>*:ls '"' => u'"' + u''.join(ls) + u'"'
|
||||
| "'" <notsquote>*:ls "'" => u"'" + u''.join(ls) + u"'"
|
||||
integer ::= '-'?:sign <digit>+:ds => int((sign if sign else '') + ''.join(ds))
|
||||
boolean ::= <false>|<true>
|
||||
false ::= 'f' 'a' 'l' 's' 'e' => False
|
||||
true ::= 't' 'r' 'u' 'e' => True
|
||||
null ::= ('n' 'u' 'l' 'l') => None
|
||||
undefined ::= ('u' 'n' 'd' 'e' 'f' 'i' 'n' 'e' 'd') => None
|
||||
notdquote ::= <escapedquote>
|
||||
| '\n' => '\\n'
|
||||
| '\r' => '\\r'
|
||||
| '\\' => '\\\\'
|
||||
| (~('"') <anything>)
|
||||
notsquote ::= <escapedquote>
|
||||
| '\n' => '\\n'
|
||||
| '\r' => '\\r'
|
||||
| '\\' => '\\\\'
|
||||
| (~("'") <anything>)
|
||||
escapedquote ::= '\\' '"' => '\\"'
|
||||
| "\\" "'" => "\\'"
|
||||
notclosebracket ::= (~(']') <anything>)
|
||||
safesymbol ::= ~<alt_inner> '['? (<letter>|'_'):start (<letterOrDigit>|'_')+:symbol ']'? => start + u''.join(symbol)
|
||||
symbol ::= ~<alt_inner> '['? (<letterOrDigit>|'-'|'@')+:symbol ']'? => u''.join(symbol)
|
||||
partialname ::= <subexpression>:s => s
|
||||
| ~<alt_inner> ('['|'"')? (~(<space>|<finish>|']'|'"' ) <anything>)+:symbol (']'|'"')? => ('literalparam', '"' + u''.join(symbol) + '"')
|
||||
pathseg ::= '[' <notclosebracket>+:symbol ']' => u''.join(symbol)
|
||||
| ('@' '.' '.' '/') => u'@@_parent'
|
||||
| <symbol>
|
||||
| '/' => u''
|
||||
| ('.' '.' '/') => u'@_parent'
|
||||
| '.' => u''
|
||||
pathfinish :expected ::= <start> '/' <path>:found ?(found == expected) <finish>
|
||||
symbolfinish :expected ::= <start> '/' <symbol>:found ?(found == expected) <finish>
|
||||
blockrule ::= <start> '#' <block_inner>:i
|
||||
<template>:t <alttemplate>:alt_t <symbolfinish i[0]> => ('block',) + i + (t, alt_t)
|
||||
| <start> '^' <block_inner>:i
|
||||
<template>:t <alttemplate>:alt_t <symbolfinish i[0]> => ('invertedblock',) + i + (t, alt_t)
|
||||
alttemplate ::= (<start> <alt_inner> <template>)?:alt_t => alt_t or []
|
||||
rawblockstart ::= <start> <start> <block_inner>:i <finish> => i
|
||||
rawblockfinish :expected ::= <start> <symbolfinish expected> <finish>
|
||||
rawblock ::= <rawblockstart>:i (~(<rawblockfinish i[0]>) <anything>)*:r <rawblockfinish i[0]>
|
||||
=> ('rawblock',) + i + (''.join(r),)
|
||||
"""
|
||||
|
||||
# this grammar compiles the template to python
|
||||
compile_grammar = """
|
||||
compile ::= <prolog> <rule>* => builder.finish()
|
||||
prolog ::= "template" => builder.start()
|
||||
rule ::= <literal>
|
||||
| <expand>
|
||||
| <escapedexpand>
|
||||
| <comment>
|
||||
| <block>
|
||||
| <invertedblock>
|
||||
| <rawblock>
|
||||
| <partial>
|
||||
block ::= [ "block" <anything>:symbol [<arg>*:arguments] [<compile>:t] [<compile>?:alt_t] ] => builder.add_block(symbol, arguments, t, alt_t)
|
||||
comment ::= [ "comment" ]
|
||||
literal ::= [ ( "literal" | "newline" | "whitespace" ) :value ] => builder.add_literal(value)
|
||||
expand ::= [ "expand" <path>:value [<arg>*:arguments]] => builder.add_expand(value, arguments)
|
||||
escapedexpand ::= [ "escapedexpand" <path>:value [<arg>*:arguments]] => builder.add_escaped_expand(value, arguments)
|
||||
invertedblock ::= [ "invertedblock" <anything>:symbol [<arg>*:arguments] [<compile>:t] [<compile>?:alt_t] ] => builder.add_invertedblock(symbol, arguments, t, alt_t)
|
||||
rawblock ::= [ "rawblock" <anything>:symbol [<arg>*:arguments] <anything>:raw ] => builder.add_rawblock(symbol, arguments, raw)
|
||||
partial ::= ["partial" <complexarg>:symbol [<arg>*:arguments]] => builder.add_partial(symbol, arguments)
|
||||
path ::= [ "path" [<pathseg>:segment]] => ("simple", segment)
|
||||
| [ "path" [<pathseg>+:segments] ] => ("complex", u"resolve(context, u'" + u"', u'".join(segments) + u"')" )
|
||||
complexarg ::= [ "path" [<pathseg>+:segments] ] => u"resolve(context, u'" + u"', u'".join(segments) + u"')"
|
||||
| [ "subexpr" ["path" <pathseg>:name] [<arg>*:arguments] ] => u'resolve_subexpr(helpers, "' + name + '", context' + (u', ' + u', '.join(arguments) if arguments else u'') + u')'
|
||||
| [ "literalparam" <anything>:value ] => {str_class}(value)
|
||||
arg ::= [ "kwparam" <anything>:symbol <complexarg>:a ] => {str_class}(symbol) + '=' + a
|
||||
| <complexarg>
|
||||
pathseg ::= "/" => ''
|
||||
| "." => ''
|
||||
| "" => ''
|
||||
| "this" => ''
|
||||
pathseg ::= <anything>:symbol => u''.join(symbol)
|
||||
""" # noqa: E501
|
||||
compile_grammar = compile_grammar.format(str_class=str_class.__name__)
|
||||
|
||||
|
||||
class PybarsError(Exception):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class strlist(list):
|
||||
|
||||
"""A quasi-list to let the template code avoid special casing."""
|
||||
|
||||
def __str__(self): # Python 3
|
||||
return ''.join(self)
|
||||
|
||||
def __unicode__(self): # Python 2
|
||||
return u''.join(self)
|
||||
|
||||
def grow(self, thing):
|
||||
"""Make the list longer, appending for unicode, extending otherwise."""
|
||||
if type(thing) == str_class:
|
||||
self.append(thing)
|
||||
|
||||
# This will only ever match in Python 2 since str_class is str in
|
||||
# Python 3.
|
||||
elif type(thing) == str:
|
||||
self.append(unicode(thing)) # noqa: F821 undefined name 'unicode'
|
||||
|
||||
else:
|
||||
# Recursively expand to a flat list; may deserve a C accelerator at
|
||||
# some point.
|
||||
for element in thing:
|
||||
self.grow(element)
|
||||
|
||||
|
||||
_map = {
|
||||
'&': '&',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'`': '`',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
}
|
||||
|
||||
|
||||
def substitute(match, _map=_map):
|
||||
return _map[match.group(0)]
|
||||
|
||||
|
||||
_escape_re = re.compile(r"&|\"|'|`|<|>")
|
||||
|
||||
|
||||
def escape(something, _escape_re=_escape_re, substitute=substitute):
|
||||
return _escape_re.sub(substitute, something)
|
||||
|
||||
|
||||
def pick(context, name, default=None):
|
||||
try:
|
||||
return context[name]
|
||||
except (KeyError, TypeError, AttributeError):
|
||||
if isinstance(name, basestring):
|
||||
try:
|
||||
exists = hasattr(context, name)
|
||||
except UnicodeEncodeError:
|
||||
# Python 2 raises UnicodeEncodeError on non-ASCII strings
|
||||
pass
|
||||
else:
|
||||
if exists:
|
||||
return getattr(context, name)
|
||||
if hasattr(context, 'get'):
|
||||
return context.get(name)
|
||||
return default
|
||||
|
||||
|
||||
sentinel = object()
|
||||
|
||||
|
||||
class Scope:
|
||||
|
||||
def __init__(self, context, parent, root, overrides=None, index=None, key=None, first=None, last=None):
|
||||
self.context = context
|
||||
self.parent = parent
|
||||
self.root = root
|
||||
# Must be dict of keys and values
|
||||
self.overrides = overrides
|
||||
self.index = index
|
||||
self.key = key
|
||||
self.first = first
|
||||
self.last = last
|
||||
|
||||
def get(self, name, default=None):
|
||||
if name == '@root':
|
||||
return self.root
|
||||
if name == '@_parent':
|
||||
return self.parent
|
||||
if name == '@index' and self.index is not None:
|
||||
return self.index
|
||||
if name == '@key' and self.key is not None:
|
||||
return self.key
|
||||
if name == '@first' and self.first is not None:
|
||||
return self.first
|
||||
if name == '@last' and self.last is not None:
|
||||
return self.last
|
||||
if name == 'this':
|
||||
return self.context
|
||||
if self.overrides and name in self.overrides:
|
||||
return self.overrides[name]
|
||||
return pick(self.context, name, default)
|
||||
__getitem__ = get
|
||||
|
||||
def __len__(self):
|
||||
return len(self.context)
|
||||
|
||||
# Added for Python 3
|
||||
def __str__(self):
|
||||
return str(self.context)
|
||||
|
||||
# Only called in Python 2
|
||||
def __unicode__(self):
|
||||
return unicode(self.context) # noqa: F821 undefined name 'unicode'
|
||||
|
||||
|
||||
def resolve(context, *segments):
|
||||
carryover_data = False
|
||||
|
||||
# This makes sure that bare "this" paths don't return a Scope object
|
||||
if segments == ('',) and isinstance(context, Scope):
|
||||
return context.get('this')
|
||||
|
||||
for segment in segments:
|
||||
|
||||
# Handle @../index syntax by popping the extra @ along the segment path
|
||||
if carryover_data:
|
||||
carryover_data = False
|
||||
segment = u'@%s' % segment
|
||||
if len(segment) > 1 and segment[0:2] == '@@':
|
||||
segment = segment[1:]
|
||||
carryover_data = True
|
||||
|
||||
if context is None:
|
||||
return None
|
||||
if segment in (None, ""):
|
||||
continue
|
||||
if type(context) in (list, tuple):
|
||||
if segment == 'length':
|
||||
return len(context)
|
||||
offset = int(segment)
|
||||
context = context[offset]
|
||||
elif isinstance(context, Scope):
|
||||
context = context.get(segment)
|
||||
else:
|
||||
context = pick(context, segment)
|
||||
return context
|
||||
|
||||
|
||||
def resolve_subexpr(helpers, name, context, *args, **kwargs):
|
||||
if name not in helpers:
|
||||
raise PybarsError(u"Could not find property %s" % (name,))
|
||||
return helpers[name](context, *args, **kwargs)
|
||||
|
||||
|
||||
def prepare(value, should_escape):
|
||||
"""
|
||||
Prepares a value to be added to the result
|
||||
|
||||
:param value:
|
||||
The value to add to the result
|
||||
|
||||
:param should_escape:
|
||||
If the string should be HTML-escaped
|
||||
|
||||
:return:
|
||||
A unicode string or strlist
|
||||
"""
|
||||
|
||||
if value is None:
|
||||
return u''
|
||||
type_ = type(value)
|
||||
if type_ is not strlist:
|
||||
if type_ is not str_class:
|
||||
if type_ is bool:
|
||||
value = u'true' if value else u'false'
|
||||
else:
|
||||
value = str_class(value)
|
||||
if should_escape:
|
||||
value = escape(value)
|
||||
return value
|
||||
|
||||
|
||||
def ensure_scope(context, root):
|
||||
return context if isinstance(context, Scope) else Scope(context, context, root)
|
||||
|
||||
|
||||
def _each(this, options, context):
|
||||
result = strlist()
|
||||
|
||||
# All sequences in python have a length
|
||||
try:
|
||||
last_index = len(context) - 1
|
||||
|
||||
# If there are no items, we want to trigger the else clause
|
||||
if last_index < 0:
|
||||
raise IndexError()
|
||||
|
||||
except (TypeError, IndexError):
|
||||
return options['inverse'](this)
|
||||
|
||||
# We use the presence of a keys method to determine if the
|
||||
# key attribute should be passed to the block handler
|
||||
has_keys = hasattr(context, 'keys')
|
||||
|
||||
index = 0
|
||||
for value in context:
|
||||
kwargs = {
|
||||
'index': index,
|
||||
'first': index == 0,
|
||||
'last': index == last_index
|
||||
}
|
||||
|
||||
if has_keys:
|
||||
kwargs['key'] = value
|
||||
value = context[value]
|
||||
|
||||
scope = Scope(value, this, options['root'], **kwargs)
|
||||
|
||||
# Necessary because of cases such as {{^each things}}test{{/each}}.
|
||||
try:
|
||||
result.grow(options['fn'](scope))
|
||||
except TypeError:
|
||||
pass
|
||||
index += 1
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _if(this, options, context):
|
||||
if hasattr(context, '__call__'):
|
||||
context = context(this)
|
||||
if context:
|
||||
return options['fn'](this)
|
||||
else:
|
||||
return options['inverse'](this)
|
||||
|
||||
|
||||
def _log(this, context):
|
||||
pybars.log(context)
|
||||
|
||||
|
||||
def _unless(this, options, context):
|
||||
if not context:
|
||||
return options['fn'](this)
|
||||
|
||||
|
||||
def _lookup(this, context, key):
|
||||
try:
|
||||
return context[key]
|
||||
except (KeyError, IndexError, TypeError):
|
||||
return
|
||||
|
||||
|
||||
def _blockHelperMissing(this, options, context):
|
||||
if hasattr(context, '__call__'):
|
||||
context = context(this)
|
||||
if context != u"" and not context:
|
||||
return options['inverse'](this)
|
||||
if type(context) in (list, strlist, tuple):
|
||||
return _each(this, options, context)
|
||||
if context is True:
|
||||
callwith = this
|
||||
else:
|
||||
callwith = context
|
||||
return options['fn'](callwith)
|
||||
|
||||
|
||||
def _helperMissing(scope, name, *args):
|
||||
if not args:
|
||||
return None
|
||||
raise PybarsError(u"Could not find property %s" % (name,))
|
||||
|
||||
|
||||
def _with(this, options, context):
|
||||
return options['fn'](context)
|
||||
|
||||
|
||||
# scope for the compiled code to reuse globals
|
||||
_pybars_ = {
|
||||
'helpers': {
|
||||
'blockHelperMissing': _blockHelperMissing,
|
||||
'each': _each,
|
||||
'if': _if,
|
||||
'helperMissing': _helperMissing,
|
||||
'log': _log,
|
||||
'unless': _unless,
|
||||
'with': _with,
|
||||
'lookup': _lookup,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class FunctionContainer:
|
||||
|
||||
"""
|
||||
Used as a container for functions by the CodeBuidler
|
||||
"""
|
||||
|
||||
def __init__(self, name, code):
|
||||
self.name = name
|
||||
self.code = code
|
||||
|
||||
@property
|
||||
def full_code(self):
|
||||
headers = (
|
||||
u'import pybars\n'
|
||||
u'\n'
|
||||
u'if pybars.__version__ != %s:\n'
|
||||
u' raise pybars.PybarsError("This template was precompiled with pybars3 version %s, running version %%s" %% pybars.__version__)\n'
|
||||
u'\n'
|
||||
u'from pybars import strlist, Scope, PybarsError\n'
|
||||
u'from pybars._compiler import _pybars_, escape, resolve, resolve_subexpr, prepare, ensure_scope\n'
|
||||
u'\n'
|
||||
u'from functools import partial\n'
|
||||
u'\n'
|
||||
u'\n'
|
||||
) % (repr(pybars.__version__), pybars.__version__)
|
||||
|
||||
return headers + self.code
|
||||
|
||||
|
||||
class CodeBuilder:
|
||||
|
||||
"""Builds code for a template."""
|
||||
|
||||
def __init__(self):
|
||||
self._reset()
|
||||
|
||||
def _reset(self):
|
||||
self.stack = []
|
||||
self.var_counter = 1
|
||||
self.render_counter = 0
|
||||
|
||||
def start(self):
|
||||
function_name = 'render' if self.render_counter == 0 else 'block_%s' % self.render_counter
|
||||
self.render_counter += 1
|
||||
|
||||
self.stack.append((strlist(), {}, function_name))
|
||||
self._result, self._locals, _ = self.stack[-1]
|
||||
# Context may be a user hash or a Scope (which injects '@_parent' to
|
||||
# implement .. lookups). The JS implementation uses a vector of scopes
|
||||
# and then interprets a linear walk-up, which is why there is a
|
||||
# disabled test showing arbitrary complex path manipulation: the scope
|
||||
# approach used here will probably DTRT but may be slower: reevaluate
|
||||
# when profiling.
|
||||
if len(self.stack) == 1:
|
||||
self._result.grow([
|
||||
u"def render(context, helpers=None, partials=None, root=None):\n"
|
||||
u" _helpers = dict(_pybars_['helpers'])\n"
|
||||
u" if helpers is not None:\n"
|
||||
u" _helpers.update(helpers)\n"
|
||||
u" helpers = _helpers\n"
|
||||
u" if partials is None:\n"
|
||||
u" partials = {}\n"
|
||||
u" called = root is None\n"
|
||||
u" if called:\n"
|
||||
u" root = context\n"
|
||||
])
|
||||
else:
|
||||
self._result.grow(u"def %s(context, helpers, partials, root):\n" % function_name)
|
||||
self._result.grow(u" result = strlist()\n")
|
||||
self._result.grow(u" context = ensure_scope(context, root)\n")
|
||||
|
||||
def finish(self):
|
||||
lines, ns, function_name = self.stack.pop(-1)
|
||||
|
||||
# Ensure the result is a string and not a strlist
|
||||
if len(self.stack) == 0:
|
||||
self._result.grow(u" if called:\n")
|
||||
self._result.grow(u" result = %s(result)\n" % str_class.__name__)
|
||||
self._result.grow(u" return result\n")
|
||||
|
||||
source = str_class(u"".join(lines))
|
||||
|
||||
self._result = self.stack and self.stack[-1][0]
|
||||
self._locals = self.stack and self.stack[-1][1]
|
||||
|
||||
code = ''
|
||||
for key in ns:
|
||||
if isinstance(ns[key], FunctionContainer):
|
||||
code += ns[key].code + '\n'
|
||||
else:
|
||||
code += '%s = %s\n' % (key, repr(ns[key]))
|
||||
code += source
|
||||
|
||||
result = FunctionContainer(function_name, code)
|
||||
if debug and len(self.stack) == 0:
|
||||
print('Compiled Python')
|
||||
print('---------------')
|
||||
print(result.full_code)
|
||||
|
||||
return result
|
||||
|
||||
def _wrap_nested(self, name):
|
||||
return u"partial(%s, helpers=helpers, partials=partials, root=root)" % name
|
||||
|
||||
def add_block(self, symbol, arguments, nested, alt_nested):
|
||||
name = nested.name
|
||||
self._locals[name] = nested
|
||||
|
||||
if alt_nested:
|
||||
alt_name = alt_nested.name
|
||||
self._locals[alt_name] = alt_nested
|
||||
|
||||
call = self.arguments_to_call(arguments)
|
||||
self._result.grow([
|
||||
u" options = {'fn': %s}\n" % self._wrap_nested(name),
|
||||
u" options['helpers'] = helpers\n"
|
||||
u" options['partials'] = partials\n"
|
||||
u" options['root'] = root\n"
|
||||
])
|
||||
if alt_nested:
|
||||
self._result.grow([
|
||||
u" options['inverse'] = ",
|
||||
self._wrap_nested(alt_name),
|
||||
u"\n"
|
||||
])
|
||||
else:
|
||||
self._result.grow([
|
||||
u" options['inverse'] = lambda this: None\n"
|
||||
])
|
||||
self._result.grow([
|
||||
u" value = helper = helpers.get(u'%s')\n" % symbol,
|
||||
u" if value is None:\n"
|
||||
u" value = resolve(context, u'%s')\n" % symbol,
|
||||
u" if helper and hasattr(helper, '__call__'):\n"
|
||||
u" value = helper(context, options%s\n" % call,
|
||||
u" else:\n"
|
||||
u" value = helpers['blockHelperMissing'](context, options, value)\n"
|
||||
u" result.grow(value or '')\n"
|
||||
])
|
||||
|
||||
def add_literal(self, value):
|
||||
self._result.grow(u" result.append(%s)\n" % repr(value))
|
||||
|
||||
def _lookup_arg(self, arg):
|
||||
if not arg:
|
||||
return u"context"
|
||||
return arg
|
||||
|
||||
def arguments_to_call(self, arguments):
|
||||
params = list(map(self._lookup_arg, arguments))
|
||||
output = u', '.join(params) + u')'
|
||||
if len(params) > 0:
|
||||
output = u', ' + output
|
||||
return output
|
||||
|
||||
def find_lookup(self, path, path_type, call):
|
||||
if path_type == "simple": # simple names can reference helpers.
|
||||
# TODO: compile this whole expression in the grammar; for now,
|
||||
# fugly but only a compile time overhead.
|
||||
# XXX: just rm.
|
||||
realname = path.replace('.get("', '').replace('")', '')
|
||||
self._result.grow([
|
||||
u" value = helpers.get(u'%s')\n" % realname,
|
||||
u" if value is None:\n"
|
||||
u" value = resolve(context, u'%s')\n" % path,
|
||||
])
|
||||
else:
|
||||
realname = None
|
||||
self._result.grow(u" value = %s\n" % path)
|
||||
self._result.grow([
|
||||
u" if hasattr(value, '__call__'):\n"
|
||||
u" value = value(context%s\n" % call,
|
||||
])
|
||||
if realname:
|
||||
self._result.grow(
|
||||
u" elif value is None:\n"
|
||||
u" value = helpers['helperMissing'](context, u'%s'%s\n"
|
||||
% (realname, call)
|
||||
)
|
||||
|
||||
def add_escaped_expand(self, path_type_path, arguments):
|
||||
(path_type, path) = path_type_path
|
||||
call = self.arguments_to_call(arguments)
|
||||
self.find_lookup(path, path_type, call)
|
||||
self._result.grow([
|
||||
u" result.grow(prepare(value, True))\n"
|
||||
])
|
||||
|
||||
def add_expand(self, path_type_path, arguments):
|
||||
(path_type, path) = path_type_path
|
||||
call = self.arguments_to_call(arguments)
|
||||
self.find_lookup(path, path_type, call)
|
||||
self._result.grow([
|
||||
u" result.grow(prepare(value, False))\n"
|
||||
])
|
||||
|
||||
def _debug(self):
|
||||
self._result.grow(u" import pdb;pdb.set_trace()\n")
|
||||
|
||||
def add_invertedblock(self, symbol, arguments, nested, alt_nested):
|
||||
name = nested.name
|
||||
self._locals[name] = nested
|
||||
|
||||
if alt_nested:
|
||||
alt_name = alt_nested.name
|
||||
self._locals[alt_name] = alt_nested
|
||||
|
||||
call = self.arguments_to_call(arguments)
|
||||
self._result.grow([
|
||||
u" options = {'inverse': %s}\n" % self._wrap_nested(name),
|
||||
u" options['helpers'] = helpers\n"
|
||||
u" options['partials'] = partials\n"
|
||||
u" options['root'] = root\n"
|
||||
])
|
||||
if alt_nested:
|
||||
self._result.grow([
|
||||
u" options['fn'] = ",
|
||||
self._wrap_nested(alt_name),
|
||||
u"\n"
|
||||
])
|
||||
else:
|
||||
self._result.grow([
|
||||
u" options['fn'] = lambda this: None\n"
|
||||
])
|
||||
self._result.grow([
|
||||
u" value = helper = helpers.get(u'%s')\n" % symbol,
|
||||
u" if value is None:\n"
|
||||
u" value = resolve(context, u'%s')\n" % symbol,
|
||||
u" if helper and hasattr(helper, '__call__'):\n"
|
||||
u" value = helper(context, options%s\n" % call,
|
||||
u" else:\n"
|
||||
u" value = helpers['blockHelperMissing'](context, options, value)\n"
|
||||
u" result.grow(value or '')\n"
|
||||
])
|
||||
|
||||
def add_rawblock(self, symbol, arguments, raw):
|
||||
call = self.arguments_to_call(arguments)
|
||||
self._result.grow([
|
||||
u" options = {'fn': lambda this: %s}\n" % repr(raw),
|
||||
u" options['helpers'] = helpers\n"
|
||||
u" options['partials'] = partials\n"
|
||||
u" options['root'] = root\n"
|
||||
u" options['inverse'] = lambda this: None\n"
|
||||
u" helper = helpers.get(u'%s')\n" % symbol,
|
||||
u" if helper and hasattr(helper, '__call__'):\n"
|
||||
u" value = helper(context, options%s\n" % call,
|
||||
u" else:\n"
|
||||
u" value = %s\n" % repr(raw),
|
||||
u" result.grow(value or '')\n"
|
||||
])
|
||||
|
||||
def _invoke_template(self, fn_name, this_name):
|
||||
self._result.grow([
|
||||
u" result.grow(",
|
||||
fn_name,
|
||||
u"(",
|
||||
this_name,
|
||||
u", helpers=helpers, partials=partials, root=root))\n"
|
||||
])
|
||||
|
||||
def add_partial(self, symbol, arguments):
|
||||
arg = ""
|
||||
|
||||
overrides = None
|
||||
positional_args = 0
|
||||
if arguments:
|
||||
for argument in arguments:
|
||||
kwmatch = re.match(r'(\w+)=(.+)$', argument)
|
||||
if kwmatch:
|
||||
if not overrides:
|
||||
overrides = {}
|
||||
overrides[kwmatch.group(1)] = kwmatch.group(2)
|
||||
else:
|
||||
if positional_args != 0:
|
||||
raise PybarsError("An extra positional argument was passed to a partial")
|
||||
positional_args += 1
|
||||
arg = argument
|
||||
|
||||
overrides_literal = 'None'
|
||||
if overrides:
|
||||
overrides_literal = u'{'
|
||||
for key in overrides:
|
||||
overrides_literal += u'"%s": %s, ' % (key, overrides[key])
|
||||
overrides_literal += u'}'
|
||||
self._result.grow([u" overrides = %s\n" % overrides_literal])
|
||||
|
||||
self._result.grow([
|
||||
u" partialName = %s\n" % symbol,
|
||||
u" if partialName not in partials:\n",
|
||||
u" raise PybarsError('The partial %s could not be found' % partialName)\n",
|
||||
u" inner = partials[partialName]\n",
|
||||
u" scope = Scope(%s, context, root, overrides=overrides)\n" % self._lookup_arg(arg)])
|
||||
self._invoke_template("inner", "scope")
|
||||
|
||||
|
||||
class Compiler:
|
||||
|
||||
"""A handlebars template compiler.
|
||||
|
||||
The compiler is not threadsafe: you need one per thread because of the
|
||||
state in CodeBuilder.
|
||||
"""
|
||||
|
||||
_handlebars = OMeta.makeGrammar(handlebars_grammar, {}, 'handlebars')
|
||||
_builder = CodeBuilder()
|
||||
_compiler = OMeta.makeGrammar(compile_grammar, {'builder': _builder})
|
||||
|
||||
def __init__(self):
|
||||
self._helpers = {}
|
||||
self.template_counter = 1
|
||||
|
||||
def _extract_word(self, source, position):
|
||||
"""
|
||||
Extracts the word that falls at or around a specific position
|
||||
|
||||
:param source:
|
||||
The template source as a unicode string
|
||||
:param position:
|
||||
The position where the word falls
|
||||
|
||||
:return:
|
||||
The word
|
||||
"""
|
||||
boundry = re.search(r'{{|{|\s|$', source[:position][::-1])
|
||||
start_offset = boundry.end() if boundry.group(0).startswith('{') else boundry.start()
|
||||
|
||||
boundry = re.search(r'}}|}|\s|$', source[position:])
|
||||
end_offset = boundry.end() if boundry.group(0).startswith('}') else boundry.start()
|
||||
|
||||
return source[position - start_offset:position + end_offset]
|
||||
|
||||
def _generate_code(self, source):
|
||||
"""
|
||||
Common compilation code shared between precompile() and compile()
|
||||
|
||||
:param source:
|
||||
The template source as a unicode string
|
||||
|
||||
:return:
|
||||
A tuple of (function, source_code)
|
||||
"""
|
||||
|
||||
if not isinstance(source, str_class):
|
||||
raise PybarsError("Template source must be a unicode string")
|
||||
|
||||
source = self.whitespace_control(source)
|
||||
|
||||
tree, (position, _) = self._handlebars(source).apply('template')
|
||||
|
||||
if debug:
|
||||
print('\nAST')
|
||||
print('---')
|
||||
print(tree)
|
||||
print('')
|
||||
|
||||
if position < len(source):
|
||||
line_num = source.count('\n') + 1
|
||||
beginning_of_line = source.rfind('\n', 0, position)
|
||||
if beginning_of_line == -1:
|
||||
char_num = position
|
||||
else:
|
||||
char_num = position - beginning_of_line
|
||||
word = self._extract_word(source, position)
|
||||
raise PybarsError("Error at character %s of line %s near %s" % (char_num, line_num, word))
|
||||
|
||||
# Ensure the builder is in a clean state - kinda gross
|
||||
self._compiler.globals['builder']._reset()
|
||||
|
||||
output = self._compiler(tree).apply('compile')[0]
|
||||
return output
|
||||
|
||||
def whitespace_control(self, source):
|
||||
"""
|
||||
Preprocess source to handle whitespace control and remove extra block
|
||||
whitespaces.
|
||||
|
||||
:param source:
|
||||
The template source as a unicode string
|
||||
:return:
|
||||
The processed template source as a unicode string
|
||||
"""
|
||||
cleanup_sub = re.compile(
|
||||
# Clean-up whitespace control marks and spaces between blocks tags
|
||||
r'(?<={{)~|~(?=}})|(?<=}})[ \t]+(?={{)').sub
|
||||
|
||||
return re.sub(
|
||||
# Whitespace control using "~" mark
|
||||
r'~}}\s*|\s*{{~|'
|
||||
|
||||
# Whitespace around alone blocks tags that in a line
|
||||
r'(?<=\n)([ \t]*{{(#[^{}]+|/[^{}]+|![^{}]+|else|else if [^{}]+)}}[ \t]*)+\r?\n|'
|
||||
|
||||
# Whitespace aroud alone blocks tag on the first line
|
||||
r'^([ \t]*{{(#[^{}]+|![^{}]+)}}[ \t]*)+\r?\n|'
|
||||
|
||||
# Whitespace aroud alone blocks tag on the last line
|
||||
r'\r?\n([ \t]*{{(/[^{}]+|![^{}]+)}}[ \t]*)+$',
|
||||
lambda match: cleanup_sub('', match.group(0).strip()), source)
|
||||
|
||||
def precompile(self, source):
|
||||
"""
|
||||
Generates python source code that can be saved to a file for caching
|
||||
|
||||
:param source:
|
||||
The template to generate source for - should be a unicode string
|
||||
|
||||
:return:
|
||||
Python code as a unicode string
|
||||
"""
|
||||
|
||||
return self._generate_code(source).full_code
|
||||
|
||||
def compile(self, source, path=None):
|
||||
"""Compile source to a ready to run template.
|
||||
|
||||
:param source:
|
||||
The template to compile - should be a unicode string
|
||||
|
||||
:return:
|
||||
A template function ready to execute
|
||||
"""
|
||||
|
||||
container = self._generate_code(source)
|
||||
|
||||
def make_module_name(name, suffix=None):
|
||||
output = 'pybars._templates.%s' % name
|
||||
if suffix:
|
||||
output += '_%s' % suffix
|
||||
return output
|
||||
|
||||
if not path:
|
||||
path = '_template'
|
||||
generate_name = True
|
||||
else:
|
||||
path = path.replace('\\', '/')
|
||||
path = path.replace('/', '_')
|
||||
mod_name = make_module_name(path)
|
||||
generate_name = mod_name in sys.modules
|
||||
|
||||
if generate_name:
|
||||
mod_name = make_module_name(path, self.template_counter)
|
||||
while mod_name in sys.modules:
|
||||
self.template_counter += 1
|
||||
mod_name = make_module_name(path, self.template_counter)
|
||||
|
||||
mod = ModuleType(mod_name)
|
||||
filename = '%s.py' % mod_name.replace('pybars.', '').replace('.', '/')
|
||||
exec(compile(container.full_code, filename, 'exec', dont_inherit=True), mod.__dict__)
|
||||
sys.modules[mod_name] = mod
|
||||
linecache.getlines(filename, mod.__dict__)
|
||||
|
||||
return mod.__dict__[container.name]
|
||||
|
||||
def template(self, code):
|
||||
def _render(context, helpers=None, partials=None, root=None):
|
||||
ns = {
|
||||
'context': context,
|
||||
'helpers': helpers,
|
||||
'partials': partials,
|
||||
'root': root
|
||||
}
|
||||
exec(code + '\nresult = render(context, helpers=helpers, partials=partials, root=root)', ns)
|
||||
return ns['result']
|
||||
return _render
|
||||
|
|
@ -0,0 +1 @@
|
|||
# This exists to create the pybars._templates module for in-memory templates
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2008-2010
|
||||
Allen Short
|
||||
Waldemar Kornewald
|
||||
|
||||
Soli Deo Gloria.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,732 @@
|
|||
# -*- test-case-name: pymeta.test.test_grammar -*-
|
||||
"""
|
||||
The definition of PyMeta's language is itself a PyMeta grammar, but something
|
||||
has to be able to read that. Most of the code in this module is generated from
|
||||
that grammar (in future versions, it will hopefully all be generated).
|
||||
"""
|
||||
import string
|
||||
from .runtime import OMetaBase, ParseError, EOFError, expected
|
||||
|
||||
|
||||
class BootOMetaGrammar(OMetaBase):
|
||||
"""
|
||||
The bootstrap grammar, generated from L{pymeta.grammar.OMetaGrammar} via
|
||||
L{pymeta.builder.PythonBuilder}.
|
||||
"""
|
||||
globals = globals()
|
||||
|
||||
def __init__(self, input):
|
||||
OMetaBase.__init__(self, input)
|
||||
self._ruleNames = []
|
||||
|
||||
|
||||
def parseGrammar(self, name, builder, *args):
|
||||
"""
|
||||
Entry point for converting a grammar to code (of some variety).
|
||||
|
||||
@param name: The name for this grammar.
|
||||
|
||||
@param builder: A class that implements the grammar-building interface
|
||||
(interface to be explicitly defined later)
|
||||
"""
|
||||
self.builder = builder(name, self, *args)
|
||||
res, err = self.apply("grammar")
|
||||
try:
|
||||
x = self.input.head()
|
||||
except EOFError:
|
||||
pass
|
||||
else:
|
||||
raise err
|
||||
return res
|
||||
|
||||
|
||||
def applicationArgs(self):
|
||||
args = []
|
||||
while True:
|
||||
try:
|
||||
(arg, endchar), err = self.pythonExpr(" >")
|
||||
if not arg:
|
||||
break
|
||||
args.append(self.builder.expr(arg))
|
||||
if endchar == '>':
|
||||
break
|
||||
except ParseError:
|
||||
break
|
||||
if args:
|
||||
return args
|
||||
else:
|
||||
raise ParseError(self.input.position, expected("python expression"))
|
||||
|
||||
|
||||
def ruleValueExpr(self):
|
||||
(expr, endchar), err = self.pythonExpr(endChars="\r\n)]")
|
||||
if str(endchar) in ")]":
|
||||
self.input = self.input.prev()
|
||||
return self.builder.expr(expr)
|
||||
|
||||
|
||||
def semanticActionExpr(self):
|
||||
return self.builder.action(self.pythonExpr(')')[0][0])
|
||||
|
||||
|
||||
def semanticPredicateExpr(self):
|
||||
expr = self.builder.expr(self.pythonExpr(')')[0][0])
|
||||
return self.builder.pred(expr)
|
||||
|
||||
|
||||
def eatWhitespace(self):
|
||||
"""
|
||||
Consume input until a non-whitespace character is reached.
|
||||
"""
|
||||
consumingComment = False
|
||||
e = None
|
||||
while True:
|
||||
try:
|
||||
c, e = self.input.head()
|
||||
except EOFError:
|
||||
break
|
||||
t = self.input.tail()
|
||||
if c.isspace() or consumingComment:
|
||||
self.input = t
|
||||
if c == '\n':
|
||||
consumingComment = False
|
||||
elif c == '#':
|
||||
consumingComment = True
|
||||
else:
|
||||
break
|
||||
return True, e
|
||||
rule_spaces = eatWhitespace
|
||||
|
||||
|
||||
def rule_number(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['number'] = _locals
|
||||
_G_apply_1, lastError = self._apply(self.rule_spaces, "spaces", [])
|
||||
self.considerError(lastError)
|
||||
def _G_or_2():
|
||||
_G_exactly_1, lastError = self.exactly('-')
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_barenumber, "barenumber", [])
|
||||
self.considerError(lastError)
|
||||
_locals['x'] = _G_apply_2
|
||||
_G_python_3, lastError = eval('self.builder.exactly(-x)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
def _G_or_3():
|
||||
_G_apply_1, lastError = self._apply(self.rule_barenumber, "barenumber", [])
|
||||
self.considerError(lastError)
|
||||
_locals['x'] = _G_apply_1
|
||||
_G_python_2, lastError = eval('self.builder.exactly(x)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
_G_or_4, lastError = self._or([_G_or_2, _G_or_3])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_4, self.currentError)
|
||||
|
||||
|
||||
def rule_barenumber(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['barenumber'] = _locals
|
||||
def _G_or_1():
|
||||
_G_exactly_1, lastError = self.exactly('0')
|
||||
self.considerError(lastError)
|
||||
def _G_or_2():
|
||||
def _G_or_1():
|
||||
_G_exactly_1, lastError = self.exactly('x')
|
||||
self.considerError(lastError)
|
||||
return (_G_exactly_1, self.currentError)
|
||||
def _G_or_2():
|
||||
_G_exactly_1, lastError = self.exactly('X')
|
||||
self.considerError(lastError)
|
||||
return (_G_exactly_1, self.currentError)
|
||||
_G_or_3, lastError = self._or([_G_or_1, _G_or_2])
|
||||
self.considerError(lastError)
|
||||
def _G_many_4():
|
||||
_G_apply_1, lastError = self._apply(self.rule_hexdigit, "hexdigit", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
_G_many_5, lastError = self.many(_G_many_4)
|
||||
self.considerError(lastError)
|
||||
_locals['hs'] = _G_many_5
|
||||
_G_python_6, lastError = eval("int(''.join(hs), 16)", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_6, self.currentError)
|
||||
def _G_or_3():
|
||||
def _G_many_1():
|
||||
_G_apply_1, lastError = self._apply(self.rule_octaldigit, "octaldigit", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
_G_many_2, lastError = self.many(_G_many_1)
|
||||
self.considerError(lastError)
|
||||
_locals['ds'] = _G_many_2
|
||||
_G_python_3, lastError = eval("int('0'+''.join(ds), 8)", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
_G_or_4, lastError = self._or([_G_or_2, _G_or_3])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_4, self.currentError)
|
||||
def _G_or_2():
|
||||
def _G_many1_1():
|
||||
_G_apply_1, lastError = self._apply(self.rule_digit, "digit", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
_G_many1_2, lastError = self.many(_G_many1_1, _G_many1_1())
|
||||
self.considerError(lastError)
|
||||
_locals['ds'] = _G_many1_2
|
||||
_G_python_3, lastError = eval("int(''.join(ds))", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
_G_or_3, lastError = self._or([_G_or_1, _G_or_2])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_3, self.currentError)
|
||||
|
||||
|
||||
def rule_octaldigit(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['octaldigit'] = _locals
|
||||
_G_apply_1, lastError = self._apply(self.rule_anything, "anything", [])
|
||||
self.considerError(lastError)
|
||||
_locals['x'] = _G_apply_1
|
||||
def _G_pred_2():
|
||||
_G_python_1, lastError = eval('x in string.octdigits', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_1, self.currentError)
|
||||
_G_pred_3, lastError = self.pred(_G_pred_2)
|
||||
self.considerError(lastError)
|
||||
_G_python_4, lastError = eval('x', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_4, self.currentError)
|
||||
|
||||
|
||||
def rule_hexdigit(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['hexdigit'] = _locals
|
||||
_G_apply_1, lastError = self._apply(self.rule_anything, "anything", [])
|
||||
self.considerError(lastError)
|
||||
_locals['x'] = _G_apply_1
|
||||
def _G_pred_2():
|
||||
_G_python_1, lastError = eval('x in string.hexdigits', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_1, self.currentError)
|
||||
_G_pred_3, lastError = self.pred(_G_pred_2)
|
||||
self.considerError(lastError)
|
||||
_G_python_4, lastError = eval('x', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_4, self.currentError)
|
||||
|
||||
|
||||
def rule_escapedChar(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['escapedChar'] = _locals
|
||||
_G_exactly_1, lastError = self.exactly('\\')
|
||||
self.considerError(lastError)
|
||||
def _G_or_2():
|
||||
_G_exactly_1, lastError = self.exactly('n')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('"\\n"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_3():
|
||||
_G_exactly_1, lastError = self.exactly('r')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('"\\r"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_4():
|
||||
_G_exactly_1, lastError = self.exactly('t')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('"\\t"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_5():
|
||||
_G_exactly_1, lastError = self.exactly('b')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('"\\b"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_6():
|
||||
_G_exactly_1, lastError = self.exactly('f')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('"\\f"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_7():
|
||||
_G_exactly_1, lastError = self.exactly('"')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('\'"\'', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_8():
|
||||
_G_exactly_1, lastError = self.exactly("'")
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('"\'"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_9():
|
||||
_G_exactly_1, lastError = self.exactly('\\')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('"\\\\"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
_G_or_10, lastError = self._or([_G_or_2, _G_or_3, _G_or_4, _G_or_5, _G_or_6, _G_or_7, _G_or_8, _G_or_9])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_10, self.currentError)
|
||||
|
||||
|
||||
def rule_character(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['character'] = _locals
|
||||
_G_python_1, lastError = eval('"\'"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
def _G_or_3():
|
||||
_G_apply_1, lastError = self._apply(self.rule_escapedChar, "escapedChar", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_4():
|
||||
_G_apply_1, lastError = self._apply(self.rule_anything, "anything", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
_G_or_5, lastError = self._or([_G_or_3, _G_or_4])
|
||||
self.considerError(lastError)
|
||||
_locals['c'] = _G_or_5
|
||||
_G_python_6, lastError = eval('"\'"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_7, lastError = self._apply(self.rule_token, "token", [_G_python_6])
|
||||
self.considerError(lastError)
|
||||
_G_python_8, lastError = eval('self.builder.exactly(c)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_8, self.currentError)
|
||||
|
||||
|
||||
def rule_string(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['string'] = _locals
|
||||
_G_python_1, lastError = eval('\'"\'', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
def _G_many_3():
|
||||
def _G_or_1():
|
||||
_G_apply_1, lastError = self._apply(self.rule_escapedChar, "escapedChar", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_2():
|
||||
def _G_not_1():
|
||||
_G_exactly_1, lastError = self.exactly('"')
|
||||
self.considerError(lastError)
|
||||
return (_G_exactly_1, self.currentError)
|
||||
_G_not_2, lastError = self._not(_G_not_1)
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_anything, "anything", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_3, self.currentError)
|
||||
_G_or_3, lastError = self._or([_G_or_1, _G_or_2])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_3, self.currentError)
|
||||
_G_many_4, lastError = self.many(_G_many_3)
|
||||
self.considerError(lastError)
|
||||
_locals['c'] = _G_many_4
|
||||
_G_python_5, lastError = eval('\'"\'', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_6, lastError = self._apply(self.rule_token, "token", [_G_python_5])
|
||||
self.considerError(lastError)
|
||||
_G_python_7, lastError = eval("self.builder.exactly(''.join(c))", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_7, self.currentError)
|
||||
|
||||
|
||||
def rule_name(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['name'] = _locals
|
||||
_G_apply_1, lastError = self._apply(self.rule_letter, "letter", [])
|
||||
self.considerError(lastError)
|
||||
_locals['x'] = _G_apply_1
|
||||
def _G_many_2():
|
||||
_G_apply_1, lastError = self._apply(self.rule_letterOrDigit, "letterOrDigit", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
_G_many_3, lastError = self.many(_G_many_2)
|
||||
self.considerError(lastError)
|
||||
_locals['xs'] = _G_many_3
|
||||
_G_python_4, lastError = eval('xs.insert(0, x)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_python_5, lastError = eval("''.join(xs)", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_5, self.currentError)
|
||||
|
||||
|
||||
def rule_application(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['application'] = _locals
|
||||
_G_python_1, lastError = eval("'<'", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_spaces, "spaces", [])
|
||||
self.considerError(lastError)
|
||||
_G_apply_4, lastError = self._apply(self.rule_name, "name", [])
|
||||
self.considerError(lastError)
|
||||
_locals['name'] = _G_apply_4
|
||||
def _G_or_5():
|
||||
_G_exactly_1, lastError = self.exactly(' ')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('self.applicationArgs()', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_locals['args'] = _G_python_2
|
||||
_G_python_3, lastError = eval('self.builder.apply(name, self.name, *args)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
def _G_or_6():
|
||||
_G_python_1, lastError = eval("'>'", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_python_3, lastError = eval('self.builder.apply(name, self.name)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
_G_or_7, lastError = self._or([_G_or_5, _G_or_6])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_7, self.currentError)
|
||||
|
||||
|
||||
def rule_expr1(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['expr1'] = _locals
|
||||
def _G_or_1():
|
||||
_G_apply_1, lastError = self._apply(self.rule_application, "application", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_2():
|
||||
_G_apply_1, lastError = self._apply(self.rule_ruleValue, "ruleValue", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_3():
|
||||
_G_apply_1, lastError = self._apply(self.rule_semanticPredicate, "semanticPredicate", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_4():
|
||||
_G_apply_1, lastError = self._apply(self.rule_semanticAction, "semanticAction", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_5():
|
||||
_G_apply_1, lastError = self._apply(self.rule_number, "number", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_6():
|
||||
_G_apply_1, lastError = self._apply(self.rule_character, "character", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_7():
|
||||
_G_apply_1, lastError = self._apply(self.rule_string, "string", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
def _G_or_8():
|
||||
_G_python_1, lastError = eval("'('", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_expr, "expr", [])
|
||||
self.considerError(lastError)
|
||||
_locals['e'] = _G_apply_3
|
||||
_G_python_4, lastError = eval("')'", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_5, lastError = self._apply(self.rule_token, "token", [_G_python_4])
|
||||
self.considerError(lastError)
|
||||
_G_python_6, lastError = eval('e', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_6, self.currentError)
|
||||
def _G_or_9():
|
||||
_G_python_1, lastError = eval("'['", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_expr, "expr", [])
|
||||
self.considerError(lastError)
|
||||
_locals['e'] = _G_apply_3
|
||||
_G_python_4, lastError = eval("']'", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_5, lastError = self._apply(self.rule_token, "token", [_G_python_4])
|
||||
self.considerError(lastError)
|
||||
_G_python_6, lastError = eval('self.builder.listpattern(e)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_6, self.currentError)
|
||||
_G_or_10, lastError = self._or([_G_or_1, _G_or_2, _G_or_3, _G_or_4, _G_or_5, _G_or_6, _G_or_7, _G_or_8, _G_or_9])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_10, self.currentError)
|
||||
|
||||
|
||||
def rule_expr2(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['expr2'] = _locals
|
||||
def _G_or_1():
|
||||
_G_python_1, lastError = eval("'~'", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
def _G_or_3():
|
||||
_G_python_1, lastError = eval("'~'", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_expr2, "expr2", [])
|
||||
self.considerError(lastError)
|
||||
_locals['e'] = _G_apply_3
|
||||
_G_python_4, lastError = eval('self.builder.lookahead(e)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_4, self.currentError)
|
||||
def _G_or_4():
|
||||
_G_apply_1, lastError = self._apply(self.rule_expr2, "expr2", [])
|
||||
self.considerError(lastError)
|
||||
_locals['e'] = _G_apply_1
|
||||
_G_python_2, lastError = eval('self.builder._not(e)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
_G_or_5, lastError = self._or([_G_or_3, _G_or_4])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_5, self.currentError)
|
||||
def _G_or_2():
|
||||
_G_apply_1, lastError = self._apply(self.rule_expr1, "expr1", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
_G_or_3, lastError = self._or([_G_or_1, _G_or_2])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_3, self.currentError)
|
||||
|
||||
|
||||
def rule_expr3(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['expr3'] = _locals
|
||||
def _G_or_1():
|
||||
_G_apply_1, lastError = self._apply(self.rule_expr2, "expr2", [])
|
||||
self.considerError(lastError)
|
||||
_locals['e'] = _G_apply_1
|
||||
def _G_or_2():
|
||||
_G_exactly_1, lastError = self.exactly('*')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('self.builder.many(e)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_3():
|
||||
_G_exactly_1, lastError = self.exactly('+')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('self.builder.many1(e)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_4():
|
||||
_G_exactly_1, lastError = self.exactly('?')
|
||||
self.considerError(lastError)
|
||||
_G_python_2, lastError = eval('self.builder.optional(e)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_2, self.currentError)
|
||||
def _G_or_5():
|
||||
_G_python_1, lastError = eval('e', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_1, self.currentError)
|
||||
_G_or_6, lastError = self._or([_G_or_2, _G_or_3, _G_or_4, _G_or_5])
|
||||
self.considerError(lastError)
|
||||
_locals['r'] = _G_or_6
|
||||
def _G_or_7():
|
||||
_G_exactly_1, lastError = self.exactly(':')
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_name, "name", [])
|
||||
self.considerError(lastError)
|
||||
_locals['n'] = _G_apply_2
|
||||
_G_python_3, lastError = eval('self.builder.bind(r, n)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
def _G_or_8():
|
||||
_G_python_1, lastError = eval('r', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_1, self.currentError)
|
||||
_G_or_9, lastError = self._or([_G_or_7, _G_or_8])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_9, self.currentError)
|
||||
def _G_or_2():
|
||||
_G_python_1, lastError = eval("':'", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_name, "name", [])
|
||||
self.considerError(lastError)
|
||||
_locals['n'] = _G_apply_3
|
||||
_G_python_4, lastError = eval('self.builder.bind(self.builder.apply("anything", self.name), n)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_4, self.currentError)
|
||||
_G_or_3, lastError = self._or([_G_or_1, _G_or_2])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_3, self.currentError)
|
||||
|
||||
|
||||
def rule_expr4(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['expr4'] = _locals
|
||||
def _G_many_1():
|
||||
_G_apply_1, lastError = self._apply(self.rule_expr3, "expr3", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
_G_many_2, lastError = self.many(_G_many_1)
|
||||
self.considerError(lastError)
|
||||
_locals['es'] = _G_many_2
|
||||
_G_python_3, lastError = eval('self.builder.sequence(es)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
|
||||
|
||||
def rule_expr(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['expr'] = _locals
|
||||
_G_apply_1, lastError = self._apply(self.rule_expr4, "expr4", [])
|
||||
self.considerError(lastError)
|
||||
_locals['e'] = _G_apply_1
|
||||
def _G_many_2():
|
||||
_G_python_1, lastError = eval("'|'", self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_expr4, "expr4", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_3, self.currentError)
|
||||
_G_many_3, lastError = self.many(_G_many_2)
|
||||
self.considerError(lastError)
|
||||
_locals['es'] = _G_many_3
|
||||
_G_python_4, lastError = eval('es.insert(0, e)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_python_5, lastError = eval('self.builder._or(es)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_5, self.currentError)
|
||||
|
||||
|
||||
def rule_ruleValue(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['ruleValue'] = _locals
|
||||
_G_python_1, lastError = eval('"=>"', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_python_3, lastError = eval('self.ruleValueExpr()', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
|
||||
|
||||
def rule_semanticPredicate(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['semanticPredicate'] = _locals
|
||||
_G_python_1, lastError = eval('"?("', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_python_3, lastError = eval('self.semanticPredicateExpr()', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
|
||||
|
||||
def rule_semanticAction(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['semanticAction'] = _locals
|
||||
_G_python_1, lastError = eval('"!("', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_python_3, lastError = eval('self.semanticActionExpr()', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
|
||||
|
||||
def rule_rulePart(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['rulePart'] = _locals
|
||||
_G_apply_1, lastError = self._apply(self.rule_anything, "anything", [])
|
||||
self.considerError(lastError)
|
||||
_locals['requiredName'] = _G_apply_1
|
||||
_G_apply_2, lastError = self._apply(self.rule_spaces, "spaces", [])
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_name, "name", [])
|
||||
self.considerError(lastError)
|
||||
_locals['n'] = _G_apply_3
|
||||
def _G_pred_4():
|
||||
_G_python_1, lastError = eval('n == requiredName', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_1, self.currentError)
|
||||
_G_pred_5, lastError = self.pred(_G_pred_4)
|
||||
self.considerError(lastError)
|
||||
_G_python_6, lastError = eval('setattr(self, "name", n)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_7, lastError = self._apply(self.rule_expr4, "expr4", [])
|
||||
self.considerError(lastError)
|
||||
_locals['args'] = _G_apply_7
|
||||
def _G_or_8():
|
||||
_G_python_1, lastError = eval('"::="', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_token, "token", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
_G_apply_3, lastError = self._apply(self.rule_expr, "expr", [])
|
||||
self.considerError(lastError)
|
||||
_locals['e'] = _G_apply_3
|
||||
_G_python_4, lastError = eval('self.builder.sequence([args, e])', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_4, self.currentError)
|
||||
def _G_or_9():
|
||||
_G_python_1, lastError = eval('args', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_1, self.currentError)
|
||||
_G_or_10, lastError = self._or([_G_or_8, _G_or_9])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_10, self.currentError)
|
||||
|
||||
|
||||
def rule_rule(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['rule'] = _locals
|
||||
_G_apply_1, lastError = self._apply(self.rule_spaces, "spaces", [])
|
||||
self.considerError(lastError)
|
||||
def _G_lookahead_2():
|
||||
_G_apply_1, lastError = self._apply(self.rule_name, "name", [])
|
||||
self.considerError(lastError)
|
||||
_locals['n'] = _G_apply_1
|
||||
return (_locals['n'], self.currentError)
|
||||
_G_lookahead_3, lastError = self.lookahead(_G_lookahead_2)
|
||||
self.considerError(lastError)
|
||||
_G_python_4, lastError = eval('n', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_5, lastError = self._apply(self.rule_rulePart, "rulePart", [_G_python_4])
|
||||
self.considerError(lastError)
|
||||
_locals['r'] = _G_apply_5
|
||||
def _G_or_6():
|
||||
def _G_many1_1():
|
||||
_G_python_1, lastError = eval('n', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
_G_apply_2, lastError = self._apply(self.rule_rulePart, "rulePart", [_G_python_1])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_2, self.currentError)
|
||||
_G_many1_2, lastError = self.many(_G_many1_1, _G_many1_1())
|
||||
self.considerError(lastError)
|
||||
_locals['rs'] = _G_many1_2
|
||||
_G_python_3, lastError = eval('self.builder.rule(n, self.builder._or([r] + rs))', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_3, self.currentError)
|
||||
def _G_or_7():
|
||||
_G_python_1, lastError = eval('self.builder.rule(n, r)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_1, self.currentError)
|
||||
_G_or_8, lastError = self._or([_G_or_6, _G_or_7])
|
||||
self.considerError(lastError)
|
||||
return (_G_or_8, self.currentError)
|
||||
|
||||
|
||||
def rule_grammar(self):
|
||||
_locals = {'self': self}
|
||||
self.locals['grammar'] = _locals
|
||||
def _G_many_1():
|
||||
_G_apply_1, lastError = self._apply(self.rule_rule, "rule", [])
|
||||
self.considerError(lastError)
|
||||
return (_G_apply_1, self.currentError)
|
||||
_G_many_2, lastError = self.many(_G_many_1)
|
||||
self.considerError(lastError)
|
||||
_locals['rs'] = _G_many_2
|
||||
_G_apply_3, lastError = self._apply(self.rule_spaces, "spaces", [])
|
||||
self.considerError(lastError)
|
||||
_G_python_4, lastError = eval('self.builder.makeGrammar(rs)', self.globals, _locals), None
|
||||
self.considerError(lastError)
|
||||
return (_G_python_4, self.currentError)
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
# -*- test-case-name: pymeta.test.test_builder -*-
|
||||
import linecache, sys
|
||||
from types import ModuleType as module
|
||||
|
||||
import itertools, linecache, sys
|
||||
|
||||
class TreeBuilder(object):
|
||||
"""
|
||||
Produce an abstract syntax tree of OMeta operations.
|
||||
"""
|
||||
|
||||
def __init__(self, name, grammar=None, *args):
|
||||
self.name = name
|
||||
|
||||
|
||||
def makeGrammar(self, rules):
|
||||
return ["Grammar", self.name, rules]
|
||||
|
||||
def rule(self, name, expr):
|
||||
return ["Rule", name, expr]
|
||||
|
||||
def apply(self, ruleName, codeName, *exprs):
|
||||
return ["Apply", ruleName, codeName, exprs]
|
||||
|
||||
def exactly(self, expr):
|
||||
return ["Exactly", expr]
|
||||
|
||||
def many(self, expr):
|
||||
return ["Many", expr]
|
||||
|
||||
def many1(self, expr):
|
||||
return ["Many1", expr]
|
||||
|
||||
def optional(self, expr):
|
||||
return ["Optional", expr]
|
||||
|
||||
def _or(self, exprs):
|
||||
return ["Or", exprs]
|
||||
|
||||
def _not(self, expr):
|
||||
return ["Not", expr]
|
||||
|
||||
def lookahead(self, expr):
|
||||
return ["Lookahead", expr]
|
||||
|
||||
def sequence(self, exprs):
|
||||
return ["And", exprs]
|
||||
|
||||
def bind(self, expr, name):
|
||||
return ["Bind", name, expr]
|
||||
|
||||
def pred(self, expr):
|
||||
return ["Predicate", expr]
|
||||
|
||||
def action(self, expr):
|
||||
return ["Action", expr]
|
||||
|
||||
def expr(self, expr):
|
||||
return ["Python", expr]
|
||||
|
||||
def listpattern(self, exprs):
|
||||
return ["List", exprs]
|
||||
|
||||
|
||||
|
||||
class PythonWriter(object):
|
||||
"""
|
||||
Converts an OMeta syntax tree into Python source.
|
||||
"""
|
||||
def __init__(self, tree):
|
||||
self.tree = tree
|
||||
self.lines = []
|
||||
self.gensymCounter = 0
|
||||
|
||||
|
||||
def _generate(self, retrn=False):
|
||||
result = self._generateNode(self.tree)
|
||||
if retrn:
|
||||
self.lines.append("return (%s, self.currentError)" % (result,))
|
||||
elif result:
|
||||
self.lines.append(result)
|
||||
return self.lines
|
||||
|
||||
|
||||
def output(self):
|
||||
return '\n'.join(self._generate())
|
||||
|
||||
|
||||
def _generateNode(self, node):
|
||||
name = node[0]
|
||||
args = node[1:]
|
||||
return getattr(self, "generate_"+name)(*args)
|
||||
|
||||
|
||||
def _gensym(self, name):
|
||||
"""
|
||||
Produce a unique name for a variable in generated code.
|
||||
"""
|
||||
self.gensymCounter += 1
|
||||
return "_G_%s_%s" % (name, self.gensymCounter)
|
||||
|
||||
|
||||
def _newThunkFor(self, name, expr):
|
||||
"""
|
||||
Define a new function of no arguments.
|
||||
@param name: The name of the rule generating this thunk.
|
||||
@param expr: A list of lines of Python code.
|
||||
"""
|
||||
|
||||
subwriter = self.__class__(expr)
|
||||
flines = subwriter._generate(retrn=True)
|
||||
fname = self._gensym(name)
|
||||
self._writeFunction(fname, (), flines)
|
||||
return fname
|
||||
|
||||
|
||||
def _expr(self, typ, e):
|
||||
"""
|
||||
Generate the code needed to execute the expression, and return the
|
||||
variable name bound to its value.
|
||||
"""
|
||||
name = self._gensym(typ)
|
||||
self.lines.append("%s, lastError = %s" % (name, e))
|
||||
self.lines.append("self.considerError(lastError)")
|
||||
return name
|
||||
|
||||
|
||||
def _writeFunction(self, fname, arglist, flines):
|
||||
"""
|
||||
Generate a function.
|
||||
@param head: The initial line defining the function.
|
||||
@param body: A list of lines for the function body.
|
||||
"""
|
||||
|
||||
self.lines.append("def %s(%s):" % (fname, ", ".join(arglist)))
|
||||
for line in flines:
|
||||
self.lines.append((" " * 4) + line)
|
||||
return fname
|
||||
|
||||
|
||||
def compilePythonExpr(self, expr):
|
||||
"""
|
||||
Generate code for running embedded Python expressions.
|
||||
"""
|
||||
|
||||
return self._expr('python', 'eval(%r, self.globals, _locals), None' %(expr,))
|
||||
|
||||
|
||||
def generate_Apply(self, ruleName, codeName, rawArgs):
|
||||
"""
|
||||
Create a call to self.apply(ruleName, *args).
|
||||
"""
|
||||
args = [self._generateNode(x) for x in rawArgs]
|
||||
if ruleName == 'super':
|
||||
return self._expr('apply', 'self.superApply("%s", %s)' % (codeName,
|
||||
', '.join(args)))
|
||||
return self._expr('apply', 'self._apply(self.rule_%s, "%s", [%s])' % (ruleName,
|
||||
ruleName,
|
||||
', '.join(args)))
|
||||
|
||||
def generate_Exactly(self, literal):
|
||||
"""
|
||||
Create a call to self.exactly(expr).
|
||||
"""
|
||||
return self._expr('exactly', 'self.exactly(%r)' % (literal,))
|
||||
|
||||
|
||||
def generate_Many(self, expr):
|
||||
"""
|
||||
Create a call to self.many(lambda: expr).
|
||||
"""
|
||||
fname = self._newThunkFor("many", expr)
|
||||
return self._expr('many', 'self.many(%s)' % (fname,))
|
||||
|
||||
|
||||
def generate_Many1(self, expr):
|
||||
"""
|
||||
Create a call to self.many(lambda: expr).
|
||||
"""
|
||||
fname = self._newThunkFor("many1", expr)
|
||||
return self._expr('many1', 'self.many(%s, %s())' % (fname, fname))
|
||||
|
||||
|
||||
def generate_Optional(self, expr):
|
||||
"""
|
||||
Try to parse an expr and continue if it fails.
|
||||
"""
|
||||
realf = self._newThunkFor("optional", expr)
|
||||
passf = self._gensym("optional")
|
||||
self._writeFunction(passf, (), ["return (None, self.input.nullError())"])
|
||||
return self._expr('or', 'self._or([%s])' % (', '.join([realf, passf])))
|
||||
|
||||
|
||||
def generate_Or(self, exprs):
|
||||
"""
|
||||
Create a call to
|
||||
self._or([lambda: expr1, lambda: expr2, ... , lambda: exprN]).
|
||||
"""
|
||||
if len(exprs) > 1:
|
||||
fnames = [self._newThunkFor("or", expr) for expr in exprs]
|
||||
return self._expr('or', 'self._or([%s])' % (', '.join(fnames)))
|
||||
else:
|
||||
return self._generateNode(exprs[0])
|
||||
|
||||
|
||||
def generate_Not(self, expr):
|
||||
"""
|
||||
Create a call to self._not(lambda: expr).
|
||||
"""
|
||||
fname = self._newThunkFor("not", expr)
|
||||
return self._expr("not", "self._not(%s)" % (fname,))
|
||||
|
||||
|
||||
def generate_Lookahead(self, expr):
|
||||
"""
|
||||
Create a call to self.lookahead(lambda: expr).
|
||||
"""
|
||||
fname = self._newThunkFor("lookahead", expr)
|
||||
return self._expr("lookahead", "self.lookahead(%s)" %(fname,))
|
||||
|
||||
|
||||
def generate_And(self, exprs):
|
||||
"""
|
||||
Generate code for each statement in order.
|
||||
"""
|
||||
v = None
|
||||
for ex in exprs:
|
||||
v = self._generateNode(ex)
|
||||
return v
|
||||
|
||||
|
||||
def generate_Bind(self, name, expr):
|
||||
"""
|
||||
Bind the value of 'expr' to a name in the _locals dict.
|
||||
"""
|
||||
v = self._generateNode(expr)
|
||||
ref = "_locals['%s']" % (name,)
|
||||
self.lines.append("%s = %s" %(ref, v))
|
||||
return ref
|
||||
|
||||
|
||||
def generate_Predicate(self, expr):
|
||||
"""
|
||||
Generate a call to self.pred(lambda: expr).
|
||||
"""
|
||||
|
||||
fname = self._newThunkFor("pred", expr)
|
||||
return self._expr("pred", "self.pred(%s)" %(fname,))
|
||||
|
||||
|
||||
def generate_Action(self, expr):
|
||||
"""
|
||||
Generate this embedded Python expression on its own line.
|
||||
"""
|
||||
return self.compilePythonExpr(expr)
|
||||
|
||||
|
||||
def generate_Python(self, expr):
|
||||
"""
|
||||
Generate this embedded Python expression on its own line.
|
||||
"""
|
||||
return self.compilePythonExpr(expr)
|
||||
|
||||
|
||||
def generate_List(self, expr):
|
||||
"""
|
||||
Generate a call to self.listpattern(lambda: expr).
|
||||
"""
|
||||
fname = self._newThunkFor("listpattern", expr)
|
||||
return self._expr("listpattern", "self.listpattern(%s)" %(fname,))
|
||||
|
||||
|
||||
def generate_Rule(self, name, expr):
|
||||
rulelines = ["_locals = {'self': self}",
|
||||
"self.locals[%r] = _locals" % (name,)]
|
||||
subwriter = self.__class__(expr)
|
||||
flines = subwriter._generate(retrn=True)
|
||||
rulelines.extend(flines)
|
||||
self._writeFunction("rule_" + name, ("self",), rulelines)
|
||||
|
||||
|
||||
def generate_Grammar(self, name, rules):
|
||||
self.lines.append("class %s(GrammarBase):" % (name,))
|
||||
for rule in rules:
|
||||
self._generateNode(rule)
|
||||
self.lines.extend(['', ''])
|
||||
self.lines[1:] = [line and (' ' * 4 + line) for line in self.lines[1:]]
|
||||
del self.lines[-2:]
|
||||
|
||||
|
||||
|
||||
def writePython(tree):
|
||||
pw = PythonWriter(tree)
|
||||
return pw.output()
|
||||
|
||||
|
||||
class GeneratedCodeLoader(object):
|
||||
"""
|
||||
Object for use as a module's __loader__, to display generated
|
||||
source.
|
||||
"""
|
||||
def __init__(self, source):
|
||||
self.source = source
|
||||
def get_source(self, name):
|
||||
return self.source
|
||||
|
||||
|
||||
|
||||
def moduleFromGrammar(tree, className, superclass, globalsDict):
|
||||
source = writePython(tree)
|
||||
modname = "pymeta_grammar__" + className
|
||||
filename = "/pymeta_generated_code/" + modname + ".py"
|
||||
mod = module(modname)
|
||||
mod.__dict__.update(globalsDict)
|
||||
mod.__name__ = modname
|
||||
mod.__dict__[superclass.__name__] = superclass
|
||||
mod.__dict__["GrammarBase"] = superclass
|
||||
mod.__loader__ = GeneratedCodeLoader(source)
|
||||
code = compile(source, filename, "exec")
|
||||
eval(code, mod.__dict__)
|
||||
fullGlobals = dict(getattr(mod.__dict__[className], "globals", None) or {})
|
||||
fullGlobals.update(globalsDict)
|
||||
mod.__dict__[className].globals = fullGlobals
|
||||
sys.modules[modname] = mod
|
||||
linecache.getlines(filename, mod.__dict__)
|
||||
return mod.__dict__[className]
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
# -*- test-case-name: pymeta.test.test_pymeta -*-
|
||||
"""
|
||||
Public interface to OMeta, as well as the grammars used to compile grammar
|
||||
definitions.
|
||||
"""
|
||||
import string
|
||||
from .builder import TreeBuilder, moduleFromGrammar
|
||||
from .boot import BootOMetaGrammar
|
||||
from .runtime import OMetaBase, ParseError, EOFError
|
||||
|
||||
class OMeta(OMetaBase):
|
||||
"""
|
||||
Base class for grammar definitions.
|
||||
"""
|
||||
metagrammarClass = BootOMetaGrammar
|
||||
def makeGrammar(cls, grammar, globals, name="Grammar"):
|
||||
"""
|
||||
Define a new subclass with the rules in the given grammar.
|
||||
|
||||
@param grammar: A string containing a PyMeta grammar.
|
||||
@param globals: A dict of names that should be accessible by this
|
||||
grammar.
|
||||
@param name: The name of the class to be generated.
|
||||
"""
|
||||
g = cls.metagrammarClass(grammar)
|
||||
tree = g.parseGrammar(name, TreeBuilder)
|
||||
return moduleFromGrammar(tree, name, cls, globals)
|
||||
|
||||
makeGrammar = classmethod(makeGrammar)
|
||||
|
||||
ometaGrammar = r"""
|
||||
number ::= <spaces> ('-' <barenumber>:x => -x
|
||||
|<barenumber>:x => x)
|
||||
barenumber ::= ('0' (('x'|'X') <hexdigit>*:hs => int(''.join(hs), 16)
|
||||
|<octaldigit>*:ds => int('0'+''.join(ds), 8))
|
||||
|<digit>+:ds => int(''.join(ds)))
|
||||
octaldigit ::= :x ?(x in string.octdigits) => x
|
||||
hexdigit ::= :x ?(x in string.hexdigits) => x
|
||||
|
||||
escapedChar ::= '\\' ('n' => "\n"
|
||||
|'r' => "\r"
|
||||
|'t' => "\t"
|
||||
|'b' => "\b"
|
||||
|'f' => "\f"
|
||||
|'"' => '"'
|
||||
|'\'' => "'"
|
||||
|'\\' => '\\')
|
||||
|
||||
character ::= <token "'"> (<escapedChar> | <anything>):c <token "'"> => c
|
||||
|
||||
bareString ::= <token '"'> (<escapedChar> | ~('"') <anything>)*:c <token '"'> => ''.join(c)
|
||||
string ::= <bareString>:s => self.builder.exactly(s)
|
||||
|
||||
name ::= <letter>:x <letterOrDigit>*:xs !(xs.insert(0, x)) => ''.join(xs)
|
||||
|
||||
application ::= (<token '<'> <spaces> <name>:name
|
||||
(' ' !(self.applicationArgs(finalChar='>')):args
|
||||
=> self.builder.apply(name, self.name, *args)
|
||||
|<token '>'>
|
||||
=> self.builder.apply(name, self.name)))
|
||||
|
||||
expr1 ::= (<application>
|
||||
|<ruleValue>
|
||||
|<semanticPredicate>
|
||||
|<semanticAction>
|
||||
|(<number> | <character>):lit => self.builder.exactly(lit)
|
||||
|<string>
|
||||
|<token '('> <expr>:e <token ')'> => e
|
||||
|<token '['> <expr>:e <token ']'> => self.builder.listpattern(e))
|
||||
|
||||
expr2 ::= (<token '~'> (<token '~'> <expr2>:e => self.builder.lookahead(e)
|
||||
|<expr2>:e => self.builder._not(e))
|
||||
|<expr1>)
|
||||
|
||||
expr3 ::= ((<expr2>:e ('*' => self.builder.many(e)
|
||||
|'+' => self.builder.many1(e)
|
||||
|'?' => self.builder.optional(e)
|
||||
| => e)):r
|
||||
(':' <name>:n => self.builder.bind(r, n)
|
||||
| => r)
|
||||
|<token ':'> <name>:n
|
||||
=> self.builder.bind(self.builder.apply("anything", self.name), n))
|
||||
|
||||
expr4 ::= <expr3>*:es => self.builder.sequence(es)
|
||||
|
||||
expr ::= <expr4>:e (<token '|'> <expr4>)*:es !(es.insert(0, e))
|
||||
=> self.builder._or(es)
|
||||
|
||||
ruleValue ::= <token "=>"> => self.ruleValueExpr(False)
|
||||
|
||||
semanticPredicate ::= <token "?("> => self.semanticPredicateExpr()
|
||||
|
||||
semanticAction ::= <token "!("> => self.semanticActionExpr()
|
||||
|
||||
rulePart :requiredName ::= (<spaces> <name>:n ?(n == requiredName)
|
||||
!(setattr(self, "name", n))
|
||||
<expr4>:args
|
||||
(<token "::="> <expr>:e
|
||||
=> self.builder.sequence([args, e])
|
||||
| => args))
|
||||
rule ::= (<spaces> ~~(<name>:n) <rulePart n>:r
|
||||
(<rulePart n>+:rs => self.builder.rule(n, self.builder._or([r] + rs))
|
||||
| => self.builder.rule(n, r)))
|
||||
|
||||
grammar ::= <rule>*:rs <spaces> => self.builder.makeGrammar(rs)
|
||||
"""
|
||||
#don't be confused, emacs
|
||||
v2Grammar = r"""
|
||||
hspace ::= (' ' | '\t')
|
||||
vspace ::= (<token "\r\n"> | '\r' | '\n')
|
||||
emptyline ::= <hspace>* <vspace>
|
||||
indentation ::= <emptyline>* <hspace>+
|
||||
noindentation ::= <emptyline>* ~~~<hspace>
|
||||
|
||||
number ::= <spaces> ('-' <barenumber>:x => self.builder.exactly(-x)
|
||||
|<barenumber>:x => self.builder.exactly(x))
|
||||
barenumber ::= '0' (('x'|'X') <hexdigit>*:hs => int(''.join(hs), 16)
|
||||
|<octaldigit>*:ds => int('0'+''.join(ds), 8))
|
||||
|<digit>+:ds => int(''.join(ds))
|
||||
octaldigit ::= :x ?(x in string.octdigits) => x
|
||||
hexdigit ::= :x ?(x in string.hexdigits) => x
|
||||
|
||||
escapedChar ::= '\\' ('n' => "\n"
|
||||
|'r' => "\r"
|
||||
|'t' => "\t"
|
||||
|'b' => "\b"
|
||||
|'f' => "\f"
|
||||
|'"' => '"'
|
||||
|'\'' => "'"
|
||||
|'\\' => "\\")
|
||||
|
||||
character ::= <token "'"> (<escapedChar> | <anything>):c <token "'"> => self.builder.exactly(c)
|
||||
|
||||
string ::= <token '"'> (<escapedChar> | ~('"') <anything>)*:c <token '"'> => self.builder.exactly(''.join(c))
|
||||
|
||||
name ::= <letter>:x <letterOrDigit>*:xs !(xs.insert(0, x)) => ''.join(xs)
|
||||
|
||||
application ::= <indentation>? <name>:name
|
||||
('(' !(self.applicationArgs(finalChar=')')):args
|
||||
=> self.builder.apply(name, self.name, *args)
|
||||
| => self.builder.apply(name, self.name))
|
||||
|
||||
expr1 ::= <application>
|
||||
|<ruleValue>
|
||||
|<semanticPredicate>
|
||||
|<semanticAction>
|
||||
|<number>
|
||||
|<character>
|
||||
|<string>
|
||||
|<token '('> <expr>:e <token ')'> => e
|
||||
|<token '['> <expr>:e <token ']'> => self.builder.listpattern(e)
|
||||
|
||||
expr2 ::= <token '~'> (<token '~'> <expr2>:e => self.builder.lookahead(e)
|
||||
|<expr2>:e => self.builder._not(e))
|
||||
|<expr1>
|
||||
|
||||
expr3 ::= (<expr2>:e ('*' => self.builder.many(e)
|
||||
|'+' => self.builder.many1(e)
|
||||
|'?' => self.builder.optional(e)
|
||||
| => e)):r
|
||||
(':' <name>:n => self.builder.bind(r, n)
|
||||
| => r)
|
||||
|<token ':'> <name>:n
|
||||
=> self.builder.bind(self.builder.apply("anything", self.name), n)
|
||||
|
||||
expr4 ::= <expr3>*:es => self.builder.sequence(es)
|
||||
|
||||
expr ::= <expr4>:e (<token '|'> <expr4>)*:es !(es.insert(0, e))
|
||||
=> self.builder._or(es)
|
||||
|
||||
ruleValue ::= <token "->"> => self.ruleValueExpr(True)
|
||||
|
||||
semanticPredicate ::= <token "?("> => self.semanticPredicateExpr()
|
||||
|
||||
semanticAction ::= <token "!("> => self.semanticActionExpr()
|
||||
|
||||
rulePart :requiredName ::= <noindentation> <name>:n ?(n == requiredName)
|
||||
!(setattr(self, "name", n))
|
||||
<expr4>:args
|
||||
(<token "="> <expr>:e
|
||||
=> self.builder.sequence([args, e])
|
||||
| => args)
|
||||
rule ::= <noindentation> ~~(<name>:n) <rulePart n>:r
|
||||
(<rulePart n>+:rs => self.builder.rule(n, self.builder._or([r] + rs))
|
||||
| => self.builder.rule(n, r))
|
||||
|
||||
grammar ::= <rule>*:rs <spaces> => self.builder.makeGrammar(rs)
|
||||
"""
|
||||
|
||||
class OMetaGrammarMixin(object):
|
||||
"""
|
||||
Helpers for the base grammar for parsing grammar definitions.
|
||||
"""
|
||||
def parseGrammar(self, name, builder, *args):
|
||||
"""
|
||||
Entry point for converting a grammar to code (of some variety).
|
||||
|
||||
@param name: The name for this grammar.
|
||||
|
||||
@param builder: A class that implements the grammar-building interface
|
||||
(interface to be explicitly defined later)
|
||||
"""
|
||||
self.builder = builder(name, self, *args)
|
||||
res, err = self.apply("grammar")
|
||||
try:
|
||||
x = self.input.head()
|
||||
except EOFError:
|
||||
pass
|
||||
else:
|
||||
raise err
|
||||
return res
|
||||
|
||||
|
||||
def applicationArgs(self, finalChar):
|
||||
"""
|
||||
Collect rule arguments, a list of Python expressions separated by
|
||||
spaces.
|
||||
"""
|
||||
args = []
|
||||
while True:
|
||||
try:
|
||||
(arg, endchar), err = self.pythonExpr(" " + finalChar)
|
||||
if not arg:
|
||||
break
|
||||
args.append(self.builder.expr(arg))
|
||||
if endchar == finalChar:
|
||||
break
|
||||
except ParseError:
|
||||
break
|
||||
if args:
|
||||
return args
|
||||
else:
|
||||
raise ParseError()
|
||||
|
||||
def ruleValueExpr(self, singleLine):
|
||||
"""
|
||||
Find and generate code for a Python expression terminated by a close
|
||||
paren/brace or end of line.
|
||||
"""
|
||||
(expr, endchar), err = self.pythonExpr(endChars="\r\n)]")
|
||||
if str(endchar) in ")]" or (singleLine and endchar):
|
||||
self.input = self.input.prev()
|
||||
return self.builder.expr(expr)
|
||||
|
||||
def semanticActionExpr(self):
|
||||
"""
|
||||
Find and generate code for a Python expression terminated by a
|
||||
close-paren, whose return value is ignored.
|
||||
"""
|
||||
return self.builder.action(self.pythonExpr(')')[0][0])
|
||||
|
||||
def semanticPredicateExpr(self):
|
||||
"""
|
||||
Find and generate code for a Python expression terminated by a
|
||||
close-paren, whose return value determines the success of the pattern
|
||||
it's in.
|
||||
"""
|
||||
expr = self.builder.expr(self.pythonExpr(')')[0][0])
|
||||
return self.builder.pred(expr)
|
||||
|
||||
|
||||
def eatWhitespace(self):
|
||||
"""
|
||||
Consume input until a non-whitespace character is reached.
|
||||
"""
|
||||
consumingComment = False
|
||||
e = None
|
||||
while True:
|
||||
try:
|
||||
c, e = self.input.head()
|
||||
except EOFError:
|
||||
break
|
||||
t = self.input.tail()
|
||||
if c.isspace() or consumingComment:
|
||||
self.input = t
|
||||
if c == '\n':
|
||||
consumingComment = False
|
||||
elif c == '#':
|
||||
consumingComment = True
|
||||
else:
|
||||
break
|
||||
return True, e
|
||||
rule_spaces = eatWhitespace
|
||||
|
||||
|
||||
|
||||
class OMetaGrammar(OMetaGrammarMixin, OMeta.makeGrammar(ometaGrammar, globals())):
|
||||
pass
|
||||
|
||||
|
||||
OMeta.metagrammarClass = OMetaGrammar
|
||||
|
||||
|
||||
class OMeta2Grammar(OMetaGrammarMixin, OMeta.makeGrammar(v2Grammar, globals())):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
nullOptimizationGrammar = """
|
||||
|
||||
opt ::= ( ["Apply" :ruleName :codeName [<anything>*:exprs]] => self.builder.apply(ruleName, codeName, *exprs)
|
||||
| ["Exactly" :expr] => self.builder.exactly(expr)
|
||||
| ["Many" <opt>:expr] => self.builder.many(expr)
|
||||
| ["Many1" <opt>:expr] => self.builder.many1(expr)
|
||||
| ["Optional" <opt>:expr] => self.builder.optional(expr)
|
||||
| ["Or" [<opt>*:exprs]] => self.builder._or(exprs)
|
||||
| ["And" [<opt>*:exprs]] => self.builder.sequence(exprs)
|
||||
| ["Not" <opt>:expr] => self.builder._not(expr)
|
||||
| ["Lookahead" <opt>:expr] => self.builder.lookahead(expr)
|
||||
| ["Bind" :name <opt>:expr] => self.builder.bind(expr, name)
|
||||
| ["Predicate" <opt>:expr] => self.builder.pred(expr)
|
||||
| ["Action" :code] => self.builder.action(code)
|
||||
| ["Python" :code] => self.builder.expr(code)
|
||||
| ["List" <opt>:exprs] => self.builder.listpattern(exprs)
|
||||
)
|
||||
grammar ::= ["Grammar" :name [<rulePair>*:rs]] => self.builder.makeGrammar(rs)
|
||||
rulePair ::= ["Rule" :name <opt>:rule] => self.builder.rule(name, rule)
|
||||
|
||||
"""
|
||||
|
||||
NullOptimizer = OMeta.makeGrammar(nullOptimizationGrammar, {}, name="NullOptimizer")
|
||||
|
|
@ -0,0 +1,612 @@
|
|||
# -*- test-case-name: pymeta.test.test_runtime -*-
|
||||
"""
|
||||
Code needed to run a grammar after it has been compiled.
|
||||
"""
|
||||
import operator
|
||||
class ParseError(Exception):
|
||||
"""
|
||||
?Redo from start
|
||||
"""
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def error(self):
|
||||
return self.args[1]
|
||||
|
||||
def __init__(self, *a):
|
||||
Exception.__init__(self, *a)
|
||||
if len(a) > 2:
|
||||
self.message = a[2]
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.args[item]
|
||||
|
||||
def __eq__(self, other):
|
||||
if other.__class__ == self.__class__:
|
||||
return (self.position, self.error) == (other.position, other.error)
|
||||
|
||||
|
||||
def formatReason(self):
|
||||
if len(self.error) == 1:
|
||||
if self.error[0][2] == None:
|
||||
return 'expected a ' + self.error[0][1]
|
||||
else:
|
||||
return 'expected the %s %s' % (self.error[0][1], self.error[0][2])
|
||||
else:
|
||||
bits = []
|
||||
for s in self.error:
|
||||
if s[2] is None:
|
||||
desc = "a " + s[1]
|
||||
else:
|
||||
desc = repr(s[2])
|
||||
if s[1] is not None:
|
||||
desc = "%s %s" % (s[1], desc)
|
||||
bits.append(desc)
|
||||
|
||||
return "expected one of %s, or %s" % (', '.join(bits[:-1]), bits[-1])
|
||||
|
||||
def formatError(self, input):
|
||||
"""
|
||||
Return a pretty string containing error info about string parsing failure.
|
||||
"""
|
||||
lines = input.split('\n')
|
||||
counter = 0
|
||||
lineNo = 1
|
||||
columnNo = 0
|
||||
for line in lines:
|
||||
newCounter = counter + len(line)
|
||||
if newCounter > self.position:
|
||||
columnNo = self.position - counter
|
||||
break
|
||||
else:
|
||||
counter += len(line) + 1
|
||||
lineNo += 1
|
||||
reason = self.formatReason()
|
||||
return ('\n' + line + '\n' + (' ' * columnNo + '^') +
|
||||
"\nParse error at line %s, column %s: %s\n" % (lineNo,
|
||||
columnNo,
|
||||
reason))
|
||||
|
||||
class EOFError(ParseError):
|
||||
def __init__(self, position):
|
||||
ParseError.__init__(self, position, eof())
|
||||
|
||||
|
||||
def expected(typ, val=None):
|
||||
"""
|
||||
Return an indication of expected input and the position where it was
|
||||
expected and not encountered.
|
||||
"""
|
||||
|
||||
return [("expected", typ, val)]
|
||||
|
||||
|
||||
def eof():
|
||||
"""
|
||||
Return an indication that the end of the input was reached.
|
||||
"""
|
||||
return [("message", "end of input")]
|
||||
|
||||
def joinErrors(errors):
|
||||
"""
|
||||
Return the error from the branch that matched the most of the input.
|
||||
"""
|
||||
def get_key(item):
|
||||
val = item[0]
|
||||
if val == None:
|
||||
val = -1000000000
|
||||
return val
|
||||
errors.sort(reverse=True, key=get_key)
|
||||
results = set()
|
||||
pos = errors[0][0]
|
||||
for err in errors:
|
||||
if pos == err[0]:
|
||||
e = err[1]
|
||||
if e is not None:
|
||||
for item in e:
|
||||
results.add(item)
|
||||
else:
|
||||
break
|
||||
|
||||
return [pos, list(results)]
|
||||
|
||||
|
||||
class character(str):
|
||||
"""
|
||||
Type to allow distinguishing characters from strings.
|
||||
"""
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
Prevent string patterns and list patterns from matching single
|
||||
characters.
|
||||
"""
|
||||
raise TypeError("Characters are not iterable")
|
||||
|
||||
try:
|
||||
_has_unicode = True
|
||||
class unicodeCharacter(unicode):
|
||||
"""
|
||||
Type to distinguish characters from Unicode strings.
|
||||
"""
|
||||
def __iter__(self):
|
||||
"""
|
||||
Prevent string patterns and list patterns from matching single
|
||||
characters.
|
||||
"""
|
||||
raise TypeError("Characters are not iterable")
|
||||
except NameError:
|
||||
_has_unicode = False
|
||||
|
||||
class InputStream(object):
|
||||
"""
|
||||
The basic input mechanism used by OMeta grammars.
|
||||
"""
|
||||
|
||||
def fromIterable(cls, iterable):
|
||||
"""
|
||||
@param iterable: Any iterable Python object.
|
||||
"""
|
||||
if isinstance(iterable, str):
|
||||
data = [character(c) for c in iterable]
|
||||
elif _has_unicode and isinstance(iterable, unicode):
|
||||
data = [unicodeCharacter(c) for c in iterable]
|
||||
else:
|
||||
data = list(iterable)
|
||||
return cls(data, 0)
|
||||
fromIterable = classmethod(fromIterable)
|
||||
|
||||
def __init__(self, data, position):
|
||||
self.data = data
|
||||
self.position = position
|
||||
self.memo = {}
|
||||
self.tl = None
|
||||
|
||||
def head(self):
|
||||
if self.position >= len(self.data):
|
||||
raise EOFError(self.position)
|
||||
return self.data[self.position], [self.position, None]
|
||||
|
||||
def nullError(self):
|
||||
return [self.position, None]
|
||||
|
||||
def tail(self):
|
||||
if self.tl is None:
|
||||
self.tl = InputStream(self.data, self.position+1)
|
||||
return self.tl
|
||||
|
||||
def prev(self):
|
||||
return InputStream(self.data, self.position-1)
|
||||
|
||||
def getMemo(self, name):
|
||||
"""
|
||||
Returns the memo record for the named rule.
|
||||
@param name: A rule name.
|
||||
"""
|
||||
return self.memo.get(name, None)
|
||||
|
||||
|
||||
def setMemo(self, name, rec):
|
||||
"""
|
||||
Store a memo record for the given value and position for the given
|
||||
rule.
|
||||
@param name: A rule name.
|
||||
@param rec: A memo record.
|
||||
"""
|
||||
self.memo[name] = rec
|
||||
return rec
|
||||
|
||||
class ArgInput(object):
|
||||
def __init__(self, arg, parent):
|
||||
self.arg = arg
|
||||
self.parent = parent
|
||||
self.memo = {}
|
||||
self.err = parent.nullError()
|
||||
|
||||
def head(self):
|
||||
try:
|
||||
x = self.arg
|
||||
except:
|
||||
import pdb; pdb. set_trace()
|
||||
return self.arg, self.err
|
||||
|
||||
def tail(self):
|
||||
return self.parent
|
||||
|
||||
|
||||
|
||||
def nullError(self):
|
||||
return self.parent.nullError()
|
||||
|
||||
|
||||
def getMemo(self, name):
|
||||
"""
|
||||
Returns the memo record for the named rule.
|
||||
@param name: A rule name.
|
||||
"""
|
||||
return self.memo.get(name, None)
|
||||
|
||||
|
||||
def setMemo(self, name, rec):
|
||||
"""
|
||||
Store a memo record for the given value and position for the given
|
||||
rule.
|
||||
@param name: A rule name.
|
||||
@param rec: A memo record.
|
||||
"""
|
||||
self.memo[name] = rec
|
||||
return rec
|
||||
|
||||
|
||||
class LeftRecursion(object):
|
||||
"""
|
||||
Marker for left recursion in a grammar rule.
|
||||
"""
|
||||
detected = False
|
||||
|
||||
class OMetaBase(object):
|
||||
"""
|
||||
Base class providing implementations of the fundamental OMeta
|
||||
operations. Built-in rules are defined here.
|
||||
"""
|
||||
globals = None
|
||||
def __init__(self, string, globals=None):
|
||||
"""
|
||||
@param string: The string to be parsed.
|
||||
|
||||
@param globals: A dictionary of names to objects, for use in evaluating
|
||||
embedded Python expressions.
|
||||
"""
|
||||
self.input = InputStream.fromIterable(string)
|
||||
self.locals = {}
|
||||
if self.globals is None:
|
||||
if globals is None:
|
||||
self.globals = {}
|
||||
else:
|
||||
self.globals = globals
|
||||
|
||||
self.currentError = self.input.nullError()
|
||||
|
||||
def considerError(self, error):
|
||||
if error and error[0] > self.currentError[0]:
|
||||
self.currentError = error
|
||||
|
||||
|
||||
def superApply(self, ruleName, *args):
|
||||
"""
|
||||
Apply the named rule as defined on this object's superclass.
|
||||
|
||||
@param ruleName: A rule name.
|
||||
"""
|
||||
r = getattr(super(self.__class__, self), "rule_"+ruleName, None)
|
||||
if r is not None:
|
||||
self.input.setMemo(ruleName, None)
|
||||
return self._apply(r, ruleName, args)
|
||||
else:
|
||||
raise NameError("No rule named '%s'" %(ruleName,))
|
||||
|
||||
def apply(self, ruleName, *args):
|
||||
"""
|
||||
Apply the named rule, optionally with some arguments.
|
||||
|
||||
@param ruleName: A rule name.
|
||||
"""
|
||||
r = getattr(self, "rule_"+ruleName, None)
|
||||
if r is not None:
|
||||
val, err = self._apply(r, ruleName, args)
|
||||
return val, ParseError(*err)
|
||||
|
||||
else:
|
||||
raise NameError("No rule named '%s'" %(ruleName,))
|
||||
|
||||
|
||||
def _apply(self, rule, ruleName, args):
|
||||
"""
|
||||
Apply a rule method to some args.
|
||||
@param rule: A method of this object.
|
||||
@param ruleName: The name of the rule invoked.
|
||||
@param args: A sequence of arguments to it.
|
||||
"""
|
||||
if args:
|
||||
if rule.__code__.co_argcount - 1 != len(args):
|
||||
for arg in args[::-1]:
|
||||
self.input = ArgInput(arg, self.input)
|
||||
return rule()
|
||||
else:
|
||||
return rule(*args)
|
||||
memoRec = self.input.getMemo(ruleName)
|
||||
if memoRec is None:
|
||||
oldPosition = self.input
|
||||
lr = LeftRecursion()
|
||||
memoRec = self.input.setMemo(ruleName, lr)
|
||||
|
||||
#print "Calling", rule
|
||||
try:
|
||||
memoRec = self.input.setMemo(ruleName,
|
||||
[rule(), self.input])
|
||||
except ParseError:
|
||||
#print "Failed", rule
|
||||
raise
|
||||
#print "Success", rule
|
||||
if lr.detected:
|
||||
sentinel = self.input
|
||||
while True:
|
||||
try:
|
||||
self.input = oldPosition
|
||||
ans = rule()
|
||||
if (self.input == sentinel):
|
||||
break
|
||||
|
||||
memoRec = oldPosition.setMemo(ruleName,
|
||||
[ans, self.input])
|
||||
except ParseError:
|
||||
break
|
||||
self.input = oldPosition
|
||||
|
||||
elif isinstance(memoRec, LeftRecursion):
|
||||
memoRec.detected = True
|
||||
raise ParseError(None, None)
|
||||
self.input = memoRec[1]
|
||||
return memoRec[0]
|
||||
|
||||
|
||||
def rule_anything(self):
|
||||
"""
|
||||
Match a single item from the input of any kind.
|
||||
"""
|
||||
h, p = self.input.head()
|
||||
self.input = self.input.tail()
|
||||
return h, p
|
||||
|
||||
def exactly(self, wanted):
|
||||
"""
|
||||
Match a single item from the input equal to the given specimen.
|
||||
|
||||
@param wanted: What to match.
|
||||
"""
|
||||
i = self.input
|
||||
val, p = self.input.head()
|
||||
self.input = self.input.tail()
|
||||
if wanted == val:
|
||||
return val, p
|
||||
else:
|
||||
self.input = i
|
||||
raise ParseError(p[0], expected(None, wanted))
|
||||
|
||||
rule_exactly = exactly
|
||||
|
||||
def many(self, fn, *initial):
|
||||
"""
|
||||
Call C{fn} until it fails to match the input. Collect the resulting
|
||||
values into a list.
|
||||
|
||||
@param fn: A callable of no arguments.
|
||||
@param initial: Initial values to populate the returned list with.
|
||||
"""
|
||||
ans = []
|
||||
e = None
|
||||
for x, e in initial:
|
||||
ans.append(x)
|
||||
while True:
|
||||
try:
|
||||
m = self.input
|
||||
v, _ = fn()
|
||||
ans.append(v)
|
||||
except ParseError:
|
||||
self.input = m
|
||||
break
|
||||
return ans, e
|
||||
|
||||
def _or(self, fns):
|
||||
"""
|
||||
Call each of a list of functions in sequence until one succeeds,
|
||||
rewinding the input between each.
|
||||
|
||||
@param fns: A list of no-argument callables.
|
||||
"""
|
||||
errors = []
|
||||
for f in fns:
|
||||
try:
|
||||
m = self.input
|
||||
ret, err = f()
|
||||
errors.append(err)
|
||||
return ret, joinErrors(errors)
|
||||
except ParseError as e:
|
||||
errors.append(e)
|
||||
self.input = m
|
||||
raise ParseError(*joinErrors(errors))
|
||||
|
||||
|
||||
def _not(self, fn):
|
||||
"""
|
||||
Call the given function. Raise ParseError iff it does not.
|
||||
|
||||
@param fn: A callable of no arguments.
|
||||
"""
|
||||
m = self.input
|
||||
try:
|
||||
fn()
|
||||
except ParseError as e:
|
||||
self.input = m
|
||||
return True, self.input.nullError()
|
||||
else:
|
||||
raise ParseError(*self.input.nullError())
|
||||
|
||||
def eatWhitespace(self):
|
||||
"""
|
||||
Consume input until a non-whitespace character is reached.
|
||||
"""
|
||||
e = None
|
||||
while True:
|
||||
try:
|
||||
c, e = self.input.head()
|
||||
except EOFError:
|
||||
break
|
||||
t = self.input.tail()
|
||||
if c.isspace():
|
||||
self.input = t
|
||||
else:
|
||||
break
|
||||
return True, e
|
||||
rule_spaces = eatWhitespace
|
||||
|
||||
|
||||
def pred(self, expr):
|
||||
"""
|
||||
Call the given function, raising ParseError if it returns false.
|
||||
|
||||
@param expr: A callable of no arguments.
|
||||
"""
|
||||
val, e = expr()
|
||||
if not val:
|
||||
raise ParseError(*e)
|
||||
else:
|
||||
return True, e
|
||||
|
||||
def listpattern(self, expr):
|
||||
"""
|
||||
Call the given function, treating the next object on the stack as an
|
||||
iterable to be used for input.
|
||||
|
||||
@param expr: A callable of no arguments.
|
||||
"""
|
||||
v, e = self.rule_anything()
|
||||
oldInput = self.input
|
||||
try:
|
||||
self.input = InputStream.fromIterable(v)
|
||||
except TypeError:
|
||||
e = self.input.nullError()
|
||||
e[1] = expected("an iterable")
|
||||
raise ParseError(*e)
|
||||
expr()
|
||||
self.end()
|
||||
self.input = oldInput
|
||||
return v, e
|
||||
|
||||
|
||||
def end(self):
|
||||
"""
|
||||
Match the end of the stream.
|
||||
"""
|
||||
return self._not(self.rule_anything)
|
||||
|
||||
rule_end = end
|
||||
|
||||
def lookahead(self, f):
|
||||
"""
|
||||
Execute the given callable, rewinding the stream no matter whether it
|
||||
returns successfully or not.
|
||||
|
||||
@param f: A callable of no arguments.
|
||||
"""
|
||||
try:
|
||||
m = self.input
|
||||
x = f()
|
||||
return x
|
||||
finally:
|
||||
self.input = m
|
||||
|
||||
|
||||
def token(self, tok):
|
||||
"""
|
||||
Match and return the given string, consuming any preceding whitespace.
|
||||
"""
|
||||
m = self.input
|
||||
try:
|
||||
self.eatWhitespace()
|
||||
for c in tok:
|
||||
v, e = self.exactly(c)
|
||||
return tok, e
|
||||
except ParseError as e:
|
||||
self.input = m
|
||||
|
||||
raise ParseError(e[0], expected("token", tok))
|
||||
|
||||
rule_token = token
|
||||
|
||||
def letter(self):
|
||||
"""
|
||||
Match a single letter.
|
||||
"""
|
||||
x, e = self.rule_anything()
|
||||
if x.isalpha():
|
||||
return x, e
|
||||
else:
|
||||
e[1] = expected("letter")
|
||||
raise ParseError(*e)
|
||||
|
||||
rule_letter = letter
|
||||
|
||||
def letterOrDigit(self):
|
||||
"""
|
||||
Match a single alphanumeric character.
|
||||
"""
|
||||
x, e = self.rule_anything()
|
||||
if x.isalnum() or x == '_':
|
||||
return x, e
|
||||
else:
|
||||
e[1] = expected("letter or digit")
|
||||
raise ParseError(*e)
|
||||
|
||||
rule_letterOrDigit = letterOrDigit
|
||||
|
||||
def digit(self):
|
||||
"""
|
||||
Match a single digit.
|
||||
"""
|
||||
x, e = self.rule_anything()
|
||||
if x.isdigit():
|
||||
return x, e
|
||||
else:
|
||||
e[1] = expected("digit")
|
||||
raise ParseError(*e)
|
||||
|
||||
rule_digit = digit
|
||||
|
||||
|
||||
def pythonExpr(self, endChars="\r\n"):
|
||||
"""
|
||||
Extract a Python expression from the input and return it.
|
||||
|
||||
@arg endChars: A set of characters delimiting the end of the expression.
|
||||
"""
|
||||
delimiters = { "(": ")", "[": "]", "{": "}"}
|
||||
stack = []
|
||||
expr = []
|
||||
lastc = None
|
||||
endchar = None
|
||||
while True:
|
||||
try:
|
||||
c, e = self.rule_anything()
|
||||
except ParseError as e:
|
||||
endchar = None
|
||||
break
|
||||
if c in endChars and len(stack) == 0:
|
||||
endchar = c
|
||||
break
|
||||
else:
|
||||
expr.append(c)
|
||||
if c in delimiters:
|
||||
stack.append(delimiters[c])
|
||||
elif len(stack) > 0 and c == stack[-1]:
|
||||
stack.pop()
|
||||
elif c in list(delimiters.values()):
|
||||
raise ParseError(self.input.position, expected("Python expression"))
|
||||
elif c in "\"'":
|
||||
while True:
|
||||
strc, stre = self.rule_anything()
|
||||
expr.append(strc)
|
||||
slashcount = 0
|
||||
while strc == '\\':
|
||||
strc, stre = self.rule_anything()
|
||||
expr.append(strc)
|
||||
slashcount += 1
|
||||
if strc == c and slashcount % 2 == 0:
|
||||
break
|
||||
|
||||
if len(stack) > 0:
|
||||
raise ParseError(self.input.position, expected("Python expression"))
|
||||
return (''.join(expr).strip(), endchar), e
|
||||
Loading…
Reference in New Issue