Skip to content

Commit

Permalink
Merge 99d478c into d3d51bd
Browse files Browse the repository at this point in the history
  • Loading branch information
thesamovar committed Jul 6, 2013
2 parents d3d51bd + 99d478c commit 3a36259
Show file tree
Hide file tree
Showing 11 changed files with 981 additions and 156 deletions.
2 changes: 1 addition & 1 deletion brian2/codegen/languages/cpp/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from ..base import Language, CodeObject
from ..templates import LanguageTemplater
from ...ast_parser import CPPNodeRenderer
from brian2.parsing.rendering import CPPNodeRenderer

logger = get_logger(__name__)
try:
Expand Down
2 changes: 1 addition & 1 deletion brian2/codegen/languages/python/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from ..base import Language, CodeObject
from ..templates import LanguageTemplater
from ...ast_parser import NumpyNodeRenderer
from brian2.parsing.rendering import NumpyNodeRenderer

__all__ = ['PythonLanguage', 'PythonCodeObject']

Expand Down
2 changes: 1 addition & 1 deletion brian2/codegen/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from sympy.printing.str import StrPrinter

from .functions.numpyfunctions import DEFAULT_FUNCTIONS, log10
from .ast_parser import SympyNodeRenderer
from brian2.parsing.rendering import SympyNodeRenderer


def parse_statement(code):
Expand Down
Empty file added brian2/parsing/__init__.py
Empty file.
147 changes: 147 additions & 0 deletions brian2/parsing/dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import ast

from brian2.utils.stringtools import deindent
from collections import namedtuple

__all__ = ['abstract_code_dependencies']

def get_read_write_funcs(parsed_code):
allids = set([])
read = set([])
write = set([])
funcs = set([])
for node in ast.walk(parsed_code):
if node.__class__ is ast.Name:
allids.add(node.id)
if node.ctx.__class__ is ast.Store:
write.add(node.id)
elif node.ctx.__class__ is ast.Load:
read.add(node.id)
else:
raise SyntaxError
elif node.__class__ is ast.Call:
funcs.add(node.func.id)

read = read-funcs

# check that there's no funky stuff going on with functions
if funcs.intersection(write):
raise SyntaxError("Cannot assign to functions in abstract code")

return allids, read, write, funcs


def abstract_code_dependencies(code, known_vars=None, known_funcs=None):
'''
Analyses identifiers used in abstract code blocks
Parameters
----------
code : str
The abstract code block.
known_vars : set
The set of known variable names.
known_funcs : set
The set of known function names.
Returns
-------
results : namedtuple with the following fields
``all``
The set of all identifiers that appear in this code block,
including functions.
``read``
The set of values that are read, excluding functions.
``write``
The set of all values that are written to.
``funcs``
The set of all function names.
``known_all``
The set of all identifiers that appear in this code block and
are known.
``known_read``
The set of known values that are read, excluding functions.
``known_write``
The set of known values that are written to.
``known_funcs``
The set of known functions that are used.
``unknown_read``
The set of all unknown variables whose values are read. Equal
to ``read-known_vars``.
``unknown_write``
The set of all unknown variables written to. Equal to
``write-known_vars``.
``unknown_funcs``
The set of all unknown function names, equal to
``funcs-known_funcs``.
``undefined_read``
The set of all unknown variables whose values are read before they
are written to. If this set is nonempty it usually indicates an
error, since a variable that is read should either have been
defined in the code block (in which case it will appear in
``newly_defined``) or already be known.
``newly_defined``
The set of all variable names which are newly defined in this
abstract code block.
'''
if known_vars is None:
known_vars = set([])
if known_funcs is None:
known_funcs = set([])
if not isinstance(known_vars, set):
known_vars = set(known_vars)
if not isinstance(known_funcs, set):
known_funcs = set(known_funcs)

code = deindent(code, docstring=True)
parsed_code = ast.parse(code, mode='exec')

# Get the list of all variables that are read from and written to,
# ignoring the order
allids, read, write, funcs = get_read_write_funcs(parsed_code)

# Now check if there are any values that are unknown and read before
# they are written to
defined = known_vars.copy()
newly_defined = set([])
undefined_read = set([])
for line in parsed_code.body:
_, cur_read, cur_write, _ = get_read_write_funcs(line)
undef = cur_read-defined
undefined_read |= undef
newly_defined |= (cur_write-defined)-undefined_read
defined |= cur_write

# Return the results as a named tuple
results = dict(
all=allids,
read=read,
write=write,
funcs=funcs,
known_all=allids.intersection(known_vars.union(known_funcs)),
known_read=read.intersection(known_vars),
known_write=write.intersection(known_vars),
known_funcs=funcs.intersection(known_funcs),
unknown_read=read-known_vars,
unknown_write=write-known_vars,
unknown_funcs=funcs-known_funcs,
undefined_read=undefined_read,
newly_defined=newly_defined,
)
return namedtuple('AbstractCodeDependencies', results.keys())(**results)


if __name__=='__main__':
code = '''
x = y+z
a = f(b)
'''
known_vars = set(['y', 'z'])
print deindent(code)
print 'known_vars:', known_vars
print
r = abstract_code_dependencies(code, known_vars)
for k, v in r.__dict__.items():
print k+':', ', '.join(list(v))

0 comments on commit 3a36259

Please sign in to comment.