Example #1
0
 def test_model_with_modules(self):
     module1 = self.create_module_with_constants("module1")
     obj = self.create_object_with_properties()
     module2 = self.create_module_with_extension("module2", Nodes(), obj)
     self.model.modules['mod1'] = module1
     self.model.modules['mod2'] = module2
     self.assertModelEquals("module module1\n" + \
                            "const name0 : type0 = 0\n" + \
                            "const name1 : type1 = 1\n" + \
                            "module module2\n" + \
                            "extend nodes with " + dump(obj) + "\n")
Example #2
0
    def visit_module(self, tree):
        assert tree.text == "MODULE"
        children = tree.getChildren()
        id = self.visit(children[0])
        module = Module(id)

        # by default a module contains the Nodes domain
        # more generic this could be triggered by a USE <domain> instruction
        module.domains['nodes'] = Nodes()

        self.model.modules[id.name] = module
        self.current_module = module

        if len(children) > 1:
            for index in range(1, len(children)):
                self.visit(children[index])
        return module
Example #3
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()
Example #4
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)
Example #5
0
def create_model():
  model = Model()
  for module in model.modules:
    if not module.domains['nodes']: module.domains['nodes'] = Nodes()
  return model
Example #6
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()
Example #7
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)
Example #8
0
 def test_module_with_extensions(self):
     obj = self.create_object_with_properties()
     module = self.create_module_with_extension("moduleName", Nodes(), obj)
     self.assertEqual(dump(module), "module moduleName\n" + \
                                   "extend nodes with " + dump(obj) + "\n")
Example #9
0
 def test_extension_with_object_extension(self):
     obj = self.create_object_with_properties()
     ext = Extension(Nodes(), obj)
     self.assertEqual(dump(ext), "extend nodes with " + dump(obj))
Example #10
0
 def test_every_strategy(self):
     function = self.create_function("name")
     strategy = Every(AllNodes(Nodes()), function, \
                      VariableExp(Identifier("interval")))
     self.assertEqual(dump(strategy), "@every(interval)\n" + \
                                     "with nodes do " + dump(function))
Example #11
0
 def test_domain_specific_scope(self):
     scope = Nodes().get_scope("self")
     self.assertEqual(dump(scope), "nodes.self")
Example #12
0
 def test_domain_scope(self):
     scope = Nodes().get_scope()
     self.assertEqual(dump(scope), "nodes")