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 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 construct(self, code_module, module): """ Constructs a code_module from a module. """ self.add_import_nodes(code_module) code_module.select("dec").append(code.Import(code_module.data)) code_module.select("def").append(code.Import("includes")) # add extensions to node_t definition node_type = self.generator.unit.find("node_type_def") node_type.append(code.Comment("extended properties for " + module.name)) # add initializations to init_node function init = self.generator.unit.find("init_node") for ext in module.domains["nodes"].extensions: for prop in ext.extension.properties: if isinstance(prop.type, model.ManyType) and \ isinstance(prop.type.subtype, model.TupleType): code.Import("tuples").insert_before(node_type) node_type.append( code.Property(prop.name, self._translate(prop.type))) init.append( code.Assign(code.ObjectProperty("node", prop.name), self.translator.translate(prop.value))) # TODO: this should go in the emission of a functioncal :-/ # and lookup the function in the externals (no time now) if isinstance(prop.value, model.FunctionCallExp) and \ prop.value.name == "now": self.generator.unit.select("node_t", "def").append( code.Import("foo-lib/time")) # create all functions for function in module.functions: code_module.select("dec").append(self._translate(function))
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 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_multi_line_comment(self): tree = code.Comment("hello\nworld") self.assertEqualToSource(tree, "/*\n hello\n world\n*/")
def test_single_line_comment(self): tree = code.Comment("hello world") self.assertEqualToSource(tree, "// hello world")