def prepare(self): """ Prepares the definition of the node type. """ # this is a generator for the Nodes domain, let's get it ;-) self.domain = SemanticNodes() # we need a translator self.translator = Translator() node_type = code.StructuredType("nodes").tag("node_type_def") node_type.append(code.Comment("domain properties"), code.Property("id", code.ByteType()), code.Property("address", code.LongType())) # the node type module = self.generator.unit.append(structure.Module("node_t")) module.select("def").append(code.Import("moose/bool")) module.select("def").append(code.Comment("THE node type"), node_type).tag("node_t-start") # provide init_node to initialise module-specific properties module.select("dec").append( code.Import("node_t"), code.Function("init_node", params=[ code.Parameter("node", type=code.ObjectType("node")) ]).tag("init_node") # filled when creating node_t ) # TODO: remove this redundant header file module = self.generator.unit.append(structure.Module("nodes")) module.select("def").append(code.Import("includes")) # add more imports to includes anchor = self.generator.unit.select("includes").select("def").find( "foo-lib-start") code.Import("nodes").insert_before(anchor).tag("requires-tuples") code.Import("node_t").insert_before(anchor) code.Import("foo-lib/nodes").insert_before(anchor) code.Import("foo-lib/payload").insert_before(anchor) # initialiase nodes and add handling of receive packets self.generator.unit.find("init").append( code.FunctionCall("nodes_init"), code.FunctionCall("mesh_on_receive", [code.SimpleVariable("payload_parser_parse")]))
def setup(self, unit): super(Demo, self).setup(unit) code.Import("../lib/application").insert_after(unit.find("main_h")) unit.find("init").append(code.Import("../lib/init.c")).stick_top() unit.find("step").append(code.Import("../lib/application_step.c")) unit.find("event_loop").append( code.FunctionCall("report_metrics")).stick_bottom()
def setup(self, unit): unit.select("includes", "def").append(code.Import("moose/avr")) unit.select("includes", "def").append(code.Import("moose/bool")) unit.select("includes", "def").append(code.Import("moose/serial")) unit.select("includes", "def").append(code.Import("moose/clock")) unit.select("includes", "def").append(code.Import("moose/xbee")) unit.find("event_loop").append(code.FunctionCall("xbee_receive"))
def test_function_call_with_arguments(self): arguments = [ code.SimpleVariable("a"), code.SimpleVariable("b"), code.BooleanLiteral(False) ] self.unit.select("test", "dec") \ .append(code.FunctionCall("some_func", arguments)) self.assertEqualToSource(self.unit, "some_func(a, b, FALSE);")
def after_visit_FunctionCallExp(self, call): # argument-expressions were pushed onto the code stack after a single # function-identifier. args = [] while isinstance(self.code[-1], code.Expression): args.append(self.code.pop()) assert isinstance(self.code[-1], code.Type) type = self.code.pop() function = self.code.pop() self.code.append( code.FunctionCall(function, list(reversed(args)), type=type))
def create_when_execution(self, execution): if execution.event.name == "receive": f = self.generator.unit.find(execution.executed.name + "_decl") if not f is None and len(f.children) > 0: # if children are single CaseStatement, it will be handled by Transform if not isinstance(f.children[0], code.CaseStatement): # else we need to register a catch-all handlers self.generator.unit.find("init").append( code.FunctionCall( "payload_parser_register_all", [code.SimpleVariable(execution.executed.name)])) return # Case-based receiving is handled by Transform # TODO: generalize: only supported = after transmit for all nodes assert execution.timing == "after" assert execution.event.name == "transmit" assert isinstance(execution.scope, AllNodes) # wire transmit handler self.generator.unit.find("init").append( code.FunctionCall("mesh_on_transmit", [code.SimpleVariable("handle_transmit")])) # setup transforming handle_transmit self.generator.unit.find("nodes_main").select("dec").append( code.Function( "handle_transmit", params=[ code.Parameter("from", code.IntegerType()), code.Parameter("hop", code.IntegerType()), code.Parameter("to", code.IntegerType()), code.Parameter("size", code.ByteType()), code.Parameter("payload", code.ManyType(code.ByteType())) ]).contains( code.FunctionCall( execution.executed.name, arguments=[ code.FunctionCall( "nodes_lookup", type=code.ObjectType("node"), arguments=[code.SimpleVariable("from")]), code.FunctionCall( "nodes_lookup", type=code.ObjectType("node"), arguments=[code.SimpleVariable("hop")]), code.FunctionCall( "nodes_lookup", type=code.ObjectType("node"), arguments=[code.SimpleVariable("to")]), code.FunctionCall( "make_payload", type=code.ObjectType("payload"), arguments=[ code.SimpleVariable("payload"), code.SimpleVariable("size") ]), ])))
def extend_main(self, module): """ Transforms the main module by adding nodes functionality to the event_loop. """ self.add_import_nodes(module) if not module.find("nodes_main") is None: return module.tag("nodes_main") # get pointers into the code dec = module.select("dec") event_loop = module.find("event_loop") # prepare top-level actions in event_loop event_loop.append(code.Comment("nodes logic execution hook")) event_loop.append(code.FunctionCall("nodes_process")) # prepare a hook to setup scheduling scheduler_init = dec.append( code.Function("nodes_scheduler_init")).tag("nodes_scheduler_init") module.find("main_function").append( code.FunctionCall(scheduler_init.name).stick_top())
def create_every_execution(self, execution): if isinstance(execution.scope, AllNodes): name = "nodes_schedule_all" else: name = "nodes_schedule_own" self.generator.unit.find("nodes_scheduler_init").append( code.FunctionCall(name, arguments=[ self._translate(execution.interval), code.SimpleVariable(execution.executed.name) ]))
def visit_CaseStatement(self, stmt): """ CaseStatements may be used to handle incoming payloads. These should be centralized in the processing of incoming payloads. Payload references are found in the case.expression.type == semantic.Nodes.payload_t. We now support the typical case with a contains() function. For each of these we generate a function based on the consequence, accepting a payload, positioned where it contains information following the case condition. The case condition's literals are added as to-find literals and a reference to the handling function is also registered with a general purpose byte- stream parser/state machine. """ # TODO: take into account execution strategy # TODO: only supported now: SimpleVariable if isinstance(stmt.expression, code.SimpleVariable): if stmt.expression.info == SemanticNodes.payload_t: # move handling to centralized processing of incoming data for case, consequence in zip(stmt.cases, stmt.consequences): # create a function that processes matched payload handler = code.Function( "nodes_process_incoming_case_" + str(Transformer.processors), params=[ code.Parameter("me", code.ObjectType("node")), code.Parameter("sender", code.ObjectType("node")), code.Parameter("from", code.ObjectType("node")), code.Parameter("hop", code.ObjectType("node")), code.Parameter("to", code.ObjectType("node")), code.Parameter( "payload", self.translate( self.domain.get_type("payload"))) ]) Transformer.processors += 1 handler.append( code.Comment("extract variables from payload")) # declare matching local variables from case # NOTE: these are inside a ListLiteral for arg in case.arguments[0]: if isinstance(arg, code.Variable): code_type = self.translate(arg.info) # TODO: generalize this more code_type_name = { "NamedType {'name': 'timestamp'}": lambda: code_type.name, "ByteType": lambda: "byte", # TODO: amount has type, should be recursively extracted # TODO: size "AmountType {}": lambda: "bytes", "ObjectType {'name': 'nodes'}": lambda: code_type.name[:-1], "FloatType": lambda: "float" }[str(code_type)]() # TODO: size should be generalized args = [code.IntegerLiteral(code_type.size)] \ if code_type_name == "bytes" else [] if isinstance(code_type, code.AmountType): code_type = code.ManyType(code.ByteType()) handler.append( code.Assign( code.VariableDecl(arg.name, code_type), code.FunctionCall( "payload_parser_consume_" + code_type_name, type=code_type, arguments=args))) # add consequence handler.append(code.Comment("perform handling actions")) for statement in consequence: handler.append(statement) self.stack[0].find("nodes_main").select("dec").append( handler) # register the handle for the literals in the case arguments = code.ListLiteral() for arg in case.arguments[0]: if isinstance(arg, code.Literal): arguments.append(arg) registration = code.FunctionCall( "payload_parser_register", [code.SimpleVariable(handler.name), arguments]) self.stack[0].find("init").append(registration) # if there is an else case, also generate it and register it if not stmt.case_else == None: handler = code.Function( "nodes_process_incoming_else", params=[ code.Parameter("sender", code.ObjectType("node")), code.Parameter("from", code.ObjectType("node")), code.Parameter("hop", code.ObjectType("node")), code.Parameter("to", code.ObjectType("node")), code.Parameter( "payload", self.translate( self.domain.get_type("payload"))) ]) handler.append(self.translate(stmt.case_else)) self.stack[0].find("nodes_main").select("dec").append( handler) registration = code.FunctionCall( "payload_parser_register_else", [code.SimpleVariable(handler.name)]) self.stack[0].find("init").append(registration) # remove the case self.stack[-2].remove_child(self.child)
def visit_MethodCall(self, call): """ Methodcalls to the nodes domain are rewritten to function-calls. """ if isinstance(call.obj, code.Object) and call.obj.name == "nodes": # FIXME: this is a bit too local, would nicer at C-level, based on the # definition of the parameters of these functions. not for now ;-) # these function call take vararg bytes. we need to convert each non-byte # argument to a ListLiteral of corresponding bytes. # create function-call # strategy: # AtomLiteral will be expanded later, so we skip them here # ObjectProperty check/convert property type # SimpleVariable # FunctionCall extract from arguments, assign to variable, convert assert isinstance(call.arguments[0], code.ListLiteral) # TODO: mark this somehow and move logic to emitter # the UnionType solution is to C-specific - no more time to do it # right :-( # rebuild the arguments args = code.ListLiteral() temp = 0 for index, arg in enumerate(call.arguments[0]): # TODO: generalize a bit more - this only support our minimal needs if isinstance(arg, code.FunctionCall): code.Assign(code.VariableDecl("temp" + str(temp), arg.type), arg) \ .insert_before(call) if isinstance(arg.type, code.AmountType) and \ isinstance(arg.type.type, code.ByteType): for i in range(arg.type.size): args.append( code.ListVariable("temp" + str(temp), i)) temp += 1 elif isinstance(arg, code.SimpleVariable): if str(arg.info) == "TimestampType": code.VariableDecl( "temp" + str(temp), code.UnionType("temp" + str(temp)) \ .contains( code.Property("value", code.NamedType("timestamp")), code.Property( "b", code.AmountType(code.ByteType(), 4) # platf? ) ) ).insert_before(call) code.Assign(code.StructProperty("temp" + str(temp), "value"), arg) \ .insert_before(call) for i in range(4): args.append( code.ListVariable( code.StructProperty( "temp" + str(temp), "b"), i)) temp += 1 elif str(arg.info) == "ObjectType(nodes)": # a node is identified in a distributed manner by its nw address # TODO: maybe not always correct, but split it out as 2 bytes args.append( code.ShiftLeft( code.ObjectProperty(arg.name, "address"), 8), code.ObjectProperty(arg.name, "address")) elif isinstance(arg, code.ObjectProperty): if isinstance(arg.type, code.ByteType): args.append(arg) elif isinstance(arg.type, code.FloatType): code.VariableDecl( "temp" + str(temp), code.UnionType("temp" + str(temp)) \ .contains( code.Property("value", code.FloatType() ), code.Property( "b", code.AmountType(code.ByteType(), 4) ) ) ).insert_before(call) code.Assign(code.StructProperty("temp" + str(temp), "value"), arg) \ .insert_before(call) for i in range(4): args.append( code.ListVariable( code.StructProperty( "temp" + str(temp), "b"), i)) temp += 1 else: args.append(arg) # replace by functioncall return code.FunctionCall("nodes_" + call.method.name, [args])
def create_main_module(self, model): """ Creates the top-level main and includes modules. """ module = self.unit.append(Module("includes").tag("includes")) # add basic set of includes that apply to all generations, without causing # compilation problems module.select("def").append(code.Import("<stdint.h>")) for mod in model.modules.values(): if len(mod.constants.items()) > 0: module.select("def").append(code.Import("constants")) break module.select("def").append( code.Import("foo-lib/crypto")).tag("foo-lib-start") module.select("def").append(code.Import("foo-lib/time")) module.select("def").append(code.Import("../lib/network")) # MAIN module module = self.unit.append(Module("main")) module.select("def").append(code.Import("includes")) module.select("dec").append(code.Import("main")).tag("main_h") for domain_module_name, domain_module in model.modules.items(): for domain_name, domain in domain_module.domains.items(): name = domain_name + "-" + domain_module_name module.select("def").append(code.Import(name)) # init init = code.Function("init").tag("init") \ .contains(code.Comment("add framework init here")) # app app = code.Function("application_step") \ .contains(code.Comment("add application specific code here")) \ .tag("step") # main main = code.Function("main", code.NamedType("int")).tag("main_function") main.append(code.FunctionCall("init").stick_top()) main.append(code.Return(code.IntegerLiteral(1))).stick_bottom() module.select("dec").append( code.Comment("""init and application_step can be implemented using application specific needs."""), init, app, code.Comment("""starting point please don't change anything beyond this point."""), main) # construct an event_loop builder and hook it into the main function event_loop = code.WhileDo(code.BooleanLiteral(True)) main.append(event_loop).tag("event_loop") \ .append(code.Comment("your application gets its share"), code.FunctionCall("application_step")) # allow each domain generator to extend the main section for mod in model.modules.values(): for domain_name, domain in mod.domains.items(): self.generator_for_domain(domain_name).extend(module)
def test_function_call_without_arguments(self): self.unit.select("test", "dec") \ .append(code.FunctionCall("some_func")) self.assertEqualToSource(self.unit, "some_func();")