Skip to content

Commit

Permalink
Add support for @staticmethod in process body.
Browse files Browse the repository at this point in the history
Issue #15 (#15).

* da/compiler/dast.py(Process):
  - (.staticmethods): new field, keep track of all static methods defined in
    the process.
  - (.staticnames): new property, set of all static method names in the
    process.

* da/compiler/parser.py(Parser):
  - (.parse_decorators): check for `@staticmethod` decorators.
  - (.visit_ClassDef): update for changed signature of `parse_decorators`.
  - (.visit_FunctionDef): add static methods to `Process.staticmethods`;
    check that entry point and "setup" methods aren't declared static.

* da/compiler/pygen.py(PythonGenerator):
  - (.visit_Process): visit the `.staticmethods` field.
  - (.visit_Function): don't inject `self` argument for static methods.
  - (.visit_NamedVar): prepend process class name for references to static
    methods.
  • Loading branch information
sadboy committed Jun 24, 2018
1 parent b69dac5 commit 29271dc
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 10 deletions.
6 changes: 6 additions & 0 deletions da/compiler/dast.py
Original file line number Diff line number Diff line change
Expand Up @@ -2504,6 +2504,8 @@ def __init__(self, parent=None, ast=None, name="", bases=[]):
self.configurations = []
# List of member methods:
self.methods = []
# List of static methods:
self.staticmethods = []
# 'setup' method:
self.setup = None
# 'main' method:
Expand All @@ -2515,6 +2517,10 @@ def __init__(self, parent=None, ast=None, name="", bases=[]):
def methodnames(self):
return {f.name for f in self.methods}

@property
def staticnames(self):
return {f.name for f in self.staticmethods}

@property
def event_handlers(self):
return list(chain(*[evt.handlers for evt in self.events]))
Expand Down
29 changes: 21 additions & 8 deletions da/compiler/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,8 @@ def parse_decorators(self, node):
labels = set()
notlabels = set()
decorators = []
is_static = False

for exp in node.decorator_list:
if isinstance(exp, Call) and isinstance(exp.func, Name) and \
exp.func.id == KW_DECORATOR_LABEL:
Expand All @@ -661,8 +663,10 @@ def parse_decorators(self, node):
else:
labels |= l
else:
if isinstance(exp, Name) and exp.id == 'staticmethod':
is_static = True
decorators.append(self.visit(exp))
return decorators, labels, notlabels
return decorators, labels, notlabels, is_static

def parse_label_spec(self, expr):
negated = False
Expand Down Expand Up @@ -852,7 +856,7 @@ def visit_ClassDef(self, node):
n = self.current_scope.add_name(node.name)
proc = dast.Process(self.current_parent, node, name=node.name)
n.add_assignment(proc, proc)
proc.decorators, _, _ = self.parse_decorators(node)
proc.decorators, _, _, _ = self.parse_decorators(node)
self.push_state(proc)
self.program.processes.append(proc)
self.program.body.append(proc)
Expand Down Expand Up @@ -887,7 +891,7 @@ def visit_ClassDef(self, node):
self.current_block.append(clsobj)
n = self.current_scope.add_name(node.name)
n.add_assignment(clsobj, clsobj)
clsobj.decorators, _, _ = self.parse_decorators(node)
clsobj.decorators, _, _, _ = self.parse_decorators(node)
self.push_state(clsobj)
clsobj.bases = self.parse_bases(node, clsobj)
self.current_block = clsobj.body
Expand All @@ -914,14 +918,25 @@ def visit_FunctionDef(self, node):
s = self.create_stmt(dast.Function, node,
params={"name" : node.name})
n.add_assignment(s, s)
# Ignore the label decorators:
s.decorators, _, _, is_static = self.parse_decorators(node)
s.process = self.current_process
if isinstance(s.parent, dast.Process):
if s.name == KW_PROCESS_ENTRY_POINT:
if is_static:
self.error("process entry point can not be static.",
node)
self.current_process.entry_point = s
elif s.name == "setup":
elif s.name == KW_SETUP:
if is_static:
self.error("%s method can not be static." % KW_SETUP,
node)
self.current_process.setup = s
else:
self.current_process.methods.append(s)
if is_static:
self.current_process.staticmethods.append(s)
else:
self.current_process.methods.append(s)
elif (isinstance(s.parent, dast.Program) and
s.name == KW_ENTRY_POINT):
# Create the node process:
Expand All @@ -933,8 +948,6 @@ def visit_FunctionDef(self, node):
# If we don't pop the entry_point from the module body then it
# ends up getting printed twice:
self.current_block.pop()
# Ignore the label decorators:
s.decorators, _, _ = self.parse_decorators(node)
self.current_block = s.body
if not self.is_in_setup():
self.signature(node.args)
Expand All @@ -951,7 +964,7 @@ def visit_FunctionDef(self, node):
h = dast.EventHandler(self.current_parent, node)
# Parse decorators before adding h to node_stack, since decorators
# should belong to the outer scope:
h.decorators, h.labels, h.notlabels = self.parse_decorators(node)
h.decorators, h.labels, h.notlabels, _ = self.parse_decorators(node)
self.push_state(h)
events, labels, notlabels = self.parse_event_handler(node)
events = self.current_process.add_events(events)
Expand Down
10 changes: 8 additions & 2 deletions da/compiler/pygen.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ def visit_Process(self, node):
if node.entry_point is not None:
cd.body.extend(self._entry_point(node.entry_point))
cd.decorator_list = [self.visit(d) for d in node.decorators]
cd.body.extend(self.body(node.staticmethods))
cd.body.extend(self.body(node.methods))
cd.body.extend(self.generate_handlers(node))
return [cd]
Expand Down Expand Up @@ -589,7 +590,8 @@ def visit_Function(self, node):
if isinstance(node.parent, dast.Process):
if node.name == "setup":
self._generate_setup(node, fd)
fd.args.args.insert(0, arg("self", None))
if node not in node.parent.staticmethods:
fd.args.args.insert(0, arg("self", None))
fd.body = self.body(node.body, fd.body)
fd.decorator_list = [self.visit(d) for d in node.decorators]
fd.returns = None
Expand Down Expand Up @@ -1015,7 +1017,11 @@ def visit_LambdaExpr(self, node):
def visit_NamedVar(self, node):
if isinstance(node.scope, dast.Process):
if node.name in node.scope.methodnames:
return pyAttr("self", node.name, self.current_context())
return pyAttr("self", node.name,
self.current_context())
elif node.name in node.scope.staticnames:
return pyAttr(node.scope.name, node.name,
self.current_context())
else:
return pyAttr(pyAttr("self", STATE_ATTR_NAME), node.name,
self.current_context())
Expand Down

0 comments on commit 29271dc

Please sign in to comment.