Exemple #1
0
    def __init__(self, args):
        try:
            self.verbose = args.verbose
        except:
            self.verbose = False

        # small limitation for now ;-)
        assert args.language == "c", "Only C language is currently implemented."

        try:
            self.output = args.output
        except:
            self.output = None
        self.platform = self.get_platform(args.platform)

        # create top-level compilation unit
        self.unit = Unit()

        # SM->CM translator
        self.translator = Translator()

        # prepare code emitter
        self.language = C.Emitter(platform=self.platform).output_to(
            self.output)

        self.domain_generators = {}
Exemple #2
0
    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")]))
Exemple #3
0
  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")])
    )
Exemple #4
0
  def __init__(self, args):
    try:    self.verbose = args.verbose
    except: self.verbose = False

    # small limitation for now ;-)
    assert args.language == "c", "Only C language is currently implemented."

    try:    self.output = args.output
    except: self.output = None
    self.platform   = self.get_platform(args.platform)

    # create top-level compilation unit
    self.unit       = Unit()

    # SM->CM translator
    self.translator = Translator()

    # prepare code emitter
    self.language   = C.Emitter(platform=self.platform).output_to(self.output)

    self.domain_generators = {}
Exemple #5
0
 def __init__(self):
     # TODO: use a backlink to generator ?
     super(Transformer, self).__init__()
     self.translator = Translator()
     # this is a Transformer for the Nodes domain, let's get it ;-)
     self.domain = SemanticNodes()
Exemple #6
0
class Transformer(language.Visitor):
    """
  Visitor for CodeCanvas-based ASTs to add/remove code automagically.
  """
    def __init__(self):
        # TODO: use a backlink to generator ?
        super(Transformer, self).__init__()
        self.translator = Translator()
        # this is a Transformer for the Nodes domain, let's get it ;-)
        self.domain = SemanticNodes()

    def translate(self, tree):
        return self.translator.translate(tree)

    @stacked
    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])

    processors = 0

    @stacked
    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)
Exemple #7
0
class Nodes(Domain):
    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 _translate(self, tree):
        return self.translator.translate(tree)

    def extend(self, module):
        """
    Add domain specific extensions (e.g. includes, functions, ...)
    """
        {"main": self.extend_main}[module.name](module)

    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 add_import_nodes(self, module):
        """
    Make sure that nodes functionality is imported (once)
    """
        if not module.find("import_nodes") is None: return
        module.select("def").append(code.Import("nodes")).tag("import_nodes")

    def transform(self, unit):
        unit.accept(Transformer())

    def link_execution(self, execution):
        {
            "Every": self.create_every_execution,
            "When": self.create_when_execution
        }[execution.__class__.__name__](execution)

    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 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")
                                ]),
                        ])))
Exemple #8
0
class Generator():
    def __init__(self, args):
        try:
            self.verbose = args.verbose
        except:
            self.verbose = False

        # small limitation for now ;-)
        assert args.language == "c", "Only C language is currently implemented."

        try:
            self.output = args.output
        except:
            self.output = None
        self.platform = self.get_platform(args.platform)

        # create top-level compilation unit
        self.unit = Unit()

        # SM->CM translator
        self.translator = Translator()

        # prepare code emitter
        self.language = C.Emitter(platform=self.platform).output_to(
            self.output)

        self.domain_generators = {}

    def __str__(self):
        return "generating to " + self.output + " using " + str(self.language) + \
                         " on " + str(self.platform)

    def log(self, msg):
        if self.verbose: print "--- " + msg

    def generate(self, model):
        self.construct_code_model(model)
        return self.emit_source()

    def construct_code_model(self, model):
        # construct basic model
        self.construct(model)

        # appy domain transformations to entire unit
        for domain_generator in self.domain_generators.values():
            domain_generator.transform(self.unit)

        return self.unit

    def emit_source(self, unit=None):
        if unit is None: unit = self.unit
        # emit to language
        self.log("starting language emission")
        return self.language.emit(unit)

    def translate(self, part):
        return self.translator.translate(part)

    def construct(self, model):
        """
    Constructs a CodeModel given a SemanticModel.
    """
        self.log("constructing basic code model from semantic model")
        self.create_main_module(model)
        # basic setup is ready, allow platform to add stuff
        self.platform.setup(self.unit)

        self.create_constants(model)
        self.create_modules(model)
        self.create_executions(model)

    def create_constants(self, model):
        defines = None
        for module in model.modules.values():
            for constant in module.constants:
                if defines is None:
                    defines = self.unit.append(
                        Module("constants")).select("def")
                defines.append(
                    code.Constant(constant.name,
                                  self.translate(constant.value),
                                  self.translate(constant.type)))

    def create_modules(self, model):
        """
    Creates a module for each domain/module pair.
    """
        for module_name, module in model.modules.items():
            for domain_name, domain in module.domains.items():
                domain_generator = self.generator_for_domain(domain_name)
                name = domain_name + "-" + module_name
                self.log("creating " + name)
                # construct section
                domain_generator.construct(self.unit.append(Module(name)),
                                           module)

    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 create_executions(self, model):
        # executions
        for module_name, module in model.modules.items():
            for execution in module.executions:
                self.generator_for_domain("nodes").link_execution(execution)

    def generator_for_domain(self, domain_name):
        """
    Lazy-Dynamic-Loading of Domain Generators, based on the Semantic Domain name
    """
        if domain_name not in self.domain_generators:
            clazz = self.get_class("foo_lang.generator.domains." + domain_name,
                                   domain_name.capitalize())
            self.domain_generators[domain_name] = clazz(self)

        return self.domain_generators[domain_name]

    def get_platform(self, platform_name):
        clazz = self.get_class("foo_lang.generator.platforms." + platform_name,
                               platform_name.capitalize())
        return clazz()

    def get_class(self, module_name, class_name):
        module = __import__(module_name, fromlist=[class_name])
        return getattr(module, class_name)
Exemple #9
0
 def __init__(self):
   # TODO: use a backlink to generator ?
   super(Transformer, self).__init__()
   self.translator = Translator()
   # this is a Transformer for the Nodes domain, let's get it ;-)
   self.domain = SemanticNodes()
Exemple #10
0
class Transformer(language.Visitor):
  """
  Visitor for CodeCanvas-based ASTs to add/remove code automagically.
  """

  def __init__(self):
    # TODO: use a backlink to generator ?
    super(Transformer, self).__init__()
    self.translator = Translator()
    # this is a Transformer for the Nodes domain, let's get it ;-)
    self.domain = SemanticNodes()

  def translate(self, tree):
    return self.translator.translate(tree)

  @stacked
  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])

  processors = 0
  @stacked
  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)
Exemple #11
0
class Nodes(Domain):
  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 _translate(self, tree):
    return self.translator.translate(tree)
  
  def extend(self, module):
    """
    Add domain specific extensions (e.g. includes, functions, ...)
    """
    {
      "main": self.extend_main
    }[module.name](module)

  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 add_import_nodes(self, module):
    """
    Make sure that nodes functionality is imported (once)
    """
    if not module.find("import_nodes") is None: return
    module.select("def").append(code.Import("nodes")).tag("import_nodes")

  def transform(self, unit):
    unit.accept(Transformer())

  def link_execution(self, execution):
    {
      "Every": self.create_every_execution,
      "When" : self.create_when_execution
    }[execution.__class__.__name__](execution)

  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 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")
            ]),
          ]
        )
      )
    )
Exemple #12
0
 def setUp(self):
     self.translator = Translator()
Exemple #13
0
class TestTranslate(unittest.TestCase):
    def setUp(self):
        self.translator = Translator()

    def translate(self, model):
        return self.translator.translate(model)

    def test_simple_types(self):
        self.assertIsInstance(self.translate(model.VoidType()), code.VoidType)
        self.assertIsInstance(self.translate(model.IntegerType()),
                              code.IntegerType)
        self.assertIsInstance(self.translate(model.FloatType()),
                              code.FloatType)
        self.assertIsInstance(self.translate(model.LongType()), code.LongType)
        self.assertIsInstance(self.translate(model.ByteType()), code.ByteType)
        self.assertIsInstance(self.translate(model.BooleanType()),
                              code.BooleanType)

    def test_known_named_types(self):
        result = self.translate(model.TimestampType())
        self.assertIsInstance(result, code.NamedType)
        self.assertEqual(result.name, "timestamp")

    def test_manytype(self):
        result = self.translate(model.ManyType(model.IntegerType()))
        self.assertIsInstance(result, code.ManyType)
        self.assertIsInstance(result.type, code.IntegerType)

    def test_nested_manytypes(self):
        result = self.translate(
            model.ManyType(model.ManyType(model.IntegerType())))
        self.assertIsInstance(result, code.ManyType)
        self.assertIsInstance(result.type, code.ManyType)
        self.assertIsInstance(result.type.type, code.IntegerType)

    def test_tupletype(self):
        result = self.translate(
            model.TupleType([model.IntegerType(),
                             model.FloatType()]))
        self.assertIsInstance(result, code.TupleType)
        self.assertIsInstance(result.types[0], code.IntegerType)
        self.assertIsInstance(result.types[1], code.FloatType)

    def test_literals(self):
        result = self.translate(model.BooleanLiteralExp(True))
        self.assertIsInstance(result, code.BooleanLiteral)
        self.assertTrue(result.value)
        result = self.translate(model.BooleanLiteralExp(False))
        self.assertIsInstance(result, code.BooleanLiteral)
        self.assertFalse(result.value)
        result = self.translate(model.IntegerLiteralExp(123))
        self.assertIsInstance(result, code.IntegerLiteral)
        self.assertEqual(result.value, 123)
        result = self.translate(model.FloatLiteralExp(12.3))
        self.assertIsInstance(result, code.FloatLiteral)
        self.assertEqual(result.value, 12.3)

    def test_variable(self):
        result = self.translate(
            model.VariableExp(model.Identifier("var_name"),
                              model.IntegerType()))
        self.assertIsInstance(result, code.SimpleVariable)
        self.assertIsInstance(result.id, code.Identifier)
        self.assertEqual(result.id.name, "var_name")

    def test_assign_stmt(self):
        result = self.translate(
            model.AssignStmt(
                model.VariableExp(model.Identifier("var_name"),
                                  model.IntegerType()),
                model.IntegerLiteralExp(456)))
        self.assertIsInstance(result, code.Assign)
        self.assertIsInstance(result.operand, code.VariableDecl)
        self.assertIsInstance(result.expression, code.IntegerLiteral)

    def test_empty_block_stmt(self):
        self.assertIsNone(self.translate(model.BlockStmt([])))

    def test_filled_block_stmt(self):
        result = self.translate(
            model.BlockStmt([
                model.AssignStmt(
                    model.VariableExp(model.Identifier("var_name"),
                                      model.IntegerType()),
                    model.IntegerLiteralExp(456))
            ]))
        self.assertEqual(len(result), 1)
        self.assertIsInstance(result[0], code.Assign)

    def test_function_without_params_or_body(self):
        result = self.translate(
            model.FunctionDecl(model.BlockStmt(), type=model.VoidType()))
        self.assertIsInstance(result, code.Function)
        self.assertIsInstance(result.type, code.VoidType)
        self.assertEqual(result.children, [])
        self.assertEqual(list(result.params), [])

    def test_function_with_params_and_body(self):
        result = self.translate(
            model.FunctionDecl(model.BlockStmt([
                model.AssignStmt(
                    model.VariableExp(model.Identifier("var_name"),
                                      model.IntegerType()),
                    model.IntegerLiteralExp(456))
            ]),
                               type=model.VoidType(),
                               parameters=[
                                   model.Parameter(
                                       model.Identifier("var_name"),
                                       model.IntegerType())
                               ]))
        self.assertIsInstance(result, code.Function)
        self.assertIsInstance(result.type, code.VoidType)
        self.assertEqual(len(result), 1)
        self.assertEqual(len(result.params), 1)
        self.assertIsInstance(result.children[0], code.Assign)
        self.assertEqual(result.params[0].name, "var_name")
        self.assertIsInstance(result.params[0].type, code.IntegerType)
Exemple #14
0
class Generator():
  def __init__(self, args):
    try:    self.verbose = args.verbose
    except: self.verbose = False

    # small limitation for now ;-)
    assert args.language == "c", "Only C language is currently implemented."

    try:    self.output = args.output
    except: self.output = None
    self.platform   = self.get_platform(args.platform)

    # create top-level compilation unit
    self.unit       = Unit()

    # SM->CM translator
    self.translator = Translator()

    # prepare code emitter
    self.language   = C.Emitter(platform=self.platform).output_to(self.output)

    self.domain_generators = {}
    
  def __str__(self):
    return "generating to " + self.output + " using " + str(self.language) + \
                     " on " + str(self.platform)

  def log(self, msg):
    if self.verbose: print "--- " + msg

  def generate(self, model):
    self.construct_code_model(model)
    return self.emit_source()

  def construct_code_model(self, model):
    # construct basic model
    self.construct(model)

    # appy domain transformations to entire unit
    for domain_generator in self.domain_generators.values():
      domain_generator.transform(self.unit)

    return self.unit

  def emit_source(self, unit=None):
    if unit is None: unit = self.unit
    # emit to language
    self.log("starting language emission")
    return self.language.emit(unit)

  def translate(self, part):
    return self.translator.translate(part)

  def construct(self, model):
    """
    Constructs a CodeModel given a SemanticModel.
    """
    self.log("constructing basic code model from semantic model")
    self.create_main_module(model)
    # basic setup is ready, allow platform to add stuff
    self.platform.setup(self.unit)
    
    self.create_constants(model)
    self.create_modules(model)
    self.create_executions(model)

  def create_constants(self, model):
    defines = None
    for module in model.modules.values():
      for constant in module.constants:
        if defines is None:
          defines = self.unit.append(Module("constants")).select("def")
        defines.append(code.Constant(constant.name,
                                     self.translate(constant.value),
                                     self.translate(constant.type)))

  def create_modules(self, model):
    """
    Creates a module for each domain/module pair.
    """
    for module_name, module in model.modules.items():
      for domain_name, domain in module.domains.items():
        domain_generator = self.generator_for_domain(domain_name)
        name = domain_name + "-" + module_name
        self.log("creating " + name)
        # construct section
        domain_generator.construct(self.unit.append(Module(name)), module)

  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 create_executions(self, model):
    # executions
    for module_name, module in model.modules.items():
      for execution in module.executions:
        self.generator_for_domain("nodes").link_execution(execution)

  def generator_for_domain(self, domain_name):
    """
    Lazy-Dynamic-Loading of Domain Generators, based on the Semantic Domain name
    """
    if domain_name not in self.domain_generators:
      clazz = self.get_class("foo_lang.generator.domains." + domain_name, 
                             domain_name.capitalize())
      self.domain_generators[domain_name] = clazz(self)
      
    return self.domain_generators[domain_name]

  def get_platform(self, platform_name):
    clazz = self.get_class("foo_lang.generator.platforms." + platform_name, 
                           platform_name.capitalize())
    return clazz()

  def get_class(self, module_name, class_name):
    module = __import__( module_name, fromlist=[class_name])
    return getattr(module, class_name)