Skip to content

Commit

Permalink
Merge 2af91f7 into 3c07c25
Browse files Browse the repository at this point in the history
  • Loading branch information
thesamovar committed Jun 22, 2013
2 parents 3c07c25 + 2af91f7 commit 9a2d2a9
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 167 deletions.
162 changes: 162 additions & 0 deletions brian2/codegen/ast_parser.py
@@ -0,0 +1,162 @@
import ast

__all__ = ['NodeRenderer',
'NumpyNodeRenderer',
'CPPNodeRenderer',
]

class NodeRenderer(object):
expression_ops = {
# BinOp
'Add': '+',
'Sub': '-',
'Mult': '*',
'Div': '/',
'Pow': '**',
'Mod': '%',
# Compare
'Lt': '<',
'LtE': '<=',
'Gt': '>',
'GtE': '>=',
'Eq': '==',
'NotEq': '!=',
# Unary ops
'Not': 'not',
'Invert': '~',
'UAdd': '+',
'USub': '-',
# Bool ops
'And': 'and',
'Or': 'or',
}

def render_expr(self, expr):
node = ast.parse(expr, mode='eval')
return self.render_node(node.body)

def render_code(self, code):
lines = []
for node in ast.parse(code).body:
lines.append(self.render_node(node))
return '\n'.join(lines)

def render_node(self, node):
nodename = node.__class__.__name__
methname = 'render_'+nodename
if not hasattr(self, methname):
raise SyntaxError("Unknown syntax: "+nodename)
return getattr(self, methname)(node)

def render_Name(self, node):
return node.id

def render_Num(self, node):
return repr(node.n)

def render_Call(self, node):
if len(node.keywords):
raise ValueError("Keyword arguments not supported.")
elif node.starargs is not None:
raise ValueError("*args not supported")
elif node.kwargs is not None:
raise ValueError("**kwds not supported")
return '%s(%s)' % (self.render_node(node.func),
', '.join(self.render_node(arg) for arg in node.args))

def render_BinOp_parentheses(self, left, right, op):
# This function checks whether or not you can ommit parentheses assuming Python
# precedence relations, hopefully this is the same in C++ and Java, but we'll need
# to check it
exprs = ['%s %s %s', '(%s) %s %s', '%s %s (%s)', '(%s) %s (%s)']
nr = NodeRenderer()
L = nr.render_node(left)
R = nr.render_node(right)
O = NodeRenderer.expression_ops[op.__class__.__name__]
refexpr = '(%s) %s (%s)' % (L, O, R)
refexprdump = ast.dump(ast.parse(refexpr))
for expr in exprs:
e = expr % (L, O, R)
if ast.dump(ast.parse(e))==refexprdump:
return expr % (self.render_node(left),
self.expression_ops[op.__class__.__name__],
self.render_node(right),
)

def render_BinOp(self, node):
return self.render_BinOp_parentheses(node.left, node.right, node.op)

def render_BoolOp(self, node):
# TODO: for the moment we always parenthesise boolean ops because precedence
# might be different in different languages and it's safer - also because it's
# a bit more complicated to write the parenthesis rule
op = node.op
left = node.values[0]
remaining = node.values[1:]
while len(remaining):
right = remaining[0]
remaining = remaining[1:]
s = self.render_BinOp_parentheses(left, right, op)
op = self.expression_ops[node.op.__class__.__name__]
return (' '+op+' ').join('(%s)' % self.render_node(v) for v in node.values)

def render_Compare(self, node):
if len(node.comparators)>1:
raise SyntaxError("Can only handle single comparisons like a<b not a<b<c")
return self.render_BinOp_parentheses(node.left, node.comparators[0], node.ops[0])

def render_UnaryOp(self, node):
return '%s(%s)' % (self.expression_ops[node.op.__class__.__name__],
self.render_node(node.operand))

def render_Assign(self, node):
if len(node.targets)>1:
raise SyntaxError("Only support syntax like a=b not a=b=c")
return '%s = %s' % (self.render_node(node.targets[0]),
self.render_node(node.value))


class NumpyNodeRenderer(NodeRenderer):
expression_ops = NodeRenderer.expression_ops.copy()
expression_ops.update({
# Unary ops
'Not': 'logical_not',
'Invert': 'logical_not',
# Bool ops
'And': '*',
'Or': '+',
})


class CPPNodeRenderer(NodeRenderer):
expression_ops = NodeRenderer.expression_ops.copy()
expression_ops.update({
# Unary ops
'Not': '!',
'Invert': '!',
# Bool ops
'And': '&&',
'Or': '||',
})

def render_BinOp(self, node):
if node.op.__class__.__name__=='Pow':
return 'pow(%s, %s)' % (self.render_node(node.left),
self.render_node(node.right))
else:
return NodeRenderer.render_BinOp(self, node)

def render_Assign(self, node):
return NodeRenderer.render_Assign(self, node)+';'


if __name__=='__main__':
# print precedence(ast.parse('c(d)**2 and 3', mode='eval').body)
# print NodeRenderer().render_expr('a-(b-c)+d')
# print NodeRenderer().render_expr('a and b or c')
for renderer in [NodeRenderer(), NumpyNodeRenderer(), CPPNodeRenderer()]:
name = renderer.__class__.__name__
print name+'\n'+'='*len(name)
print renderer.render_expr('a+b*c(d, e)+e**f')
print renderer.render_expr('a and -b and c and 1.2')
print renderer.render_code('a=b\nc=d+e')
2 changes: 0 additions & 2 deletions brian2/codegen/languages/__init__.py
@@ -1,5 +1,3 @@
from .base import *
from .cpp import *
from .cuda import *
from .python import *
from .python_numexpr import *
10 changes: 2 additions & 8 deletions brian2/codegen/languages/cpp.py
Expand Up @@ -3,15 +3,14 @@
'''
import re

from sympy.printing.ccode import CCodePrinter
import numpy

from brian2.utils.stringtools import deindent
from brian2.codegen.parsing import parse_to_sympy
from brian2.codegen.functions.base import Function
from brian2.utils.logger import get_logger

from .base import Language, CodeObject
from ..ast_parser import CPPNodeRenderer

logger = get_logger(__name__)
try:
Expand Down Expand Up @@ -111,12 +110,7 @@ def __init__(self, compiler='gcc', extra_compile_args=['-O3', '-ffast-math'],
self.flush_denormals = flush_denormals

def translate_expression(self, expr):
# temporary hack to make randn() pass through sympy
expr = re.sub(r'\brandn\b\s*\(\s*\)', '_temporary_randn_symbol', expr)
expr = parse_to_sympy(expr)
expr = CCodePrinter().doprint(expr)
expr = re.sub(r'\b_temporary_randn_symbol\b', 'randn()', expr)
return expr
return CPPNodeRenderer().render_expr(expr).strip()

def translate_statement(self, statement):
var, op, expr = statement.var, statement.op, statement.expr
Expand Down
51 changes: 0 additions & 51 deletions brian2/codegen/languages/cuda.py

This file was deleted.

1 change: 0 additions & 1 deletion brian2/codegen/languages/cython.py

This file was deleted.

3 changes: 2 additions & 1 deletion brian2/codegen/languages/python.py
@@ -1,4 +1,5 @@
from .base import Language, CodeObject
from ..ast_parser import NumpyNodeRenderer

__all__ = ['PythonLanguage', 'PythonCodeObject']

Expand All @@ -8,7 +9,7 @@ class PythonLanguage(Language):
language_id = 'python'

def translate_expression(self, expr):
return expr.strip()
return NumpyNodeRenderer().render_expr(expr).strip()

def translate_statement(self, statement):
# TODO: optimisation, translate arithmetic to a sequence of inplace
Expand Down
102 changes: 0 additions & 102 deletions brian2/codegen/languages/python_numexpr.py

This file was deleted.

1 change: 0 additions & 1 deletion brian2/codegen/languages/theano.py

This file was deleted.

0 comments on commit 9a2d2a9

Please sign in to comment.