Home > N++ > N++ execution model

N++ execution model

December 24, 2014 Leave a comment Go to comments

When creating programming languages or translation-modules, it’s very important to allow the thought to grow and mature before you jump in and build the final product.

program("service_test") {

  handshake {

    input {
      /* Consume WSDL Web-Service Endpoint */
      service1 @ service[URI:"http://www.test.com/SOAP/myService/WSDL/",
                 serviceType:SoapService];
    }

    output {
        myProcess @ process("self");
        stdio @ pipe("stdout");
      }
  }

  /* Execute RPC call */
  execute (stdio,service1)  {
    stdio:writeln("Calling webservice");
    execute (*)  {
      var int32 result = 0;
      set result = service1:getUserId("quartex","secret");
      stdio:writelnF("UserID on server={0}", result);
    } fail (e) {
      stdio.writelnF("Calling soap service failed: {0}",e);
      proceed;
    }
  }

  /* Exit code for process */
  set myProcess:exitCode = 0;
}

The smallest detail can become either a strength or a weakness for the language. For instance, in the past couple of days I have realized the following:

First: Using “:” instead of “.” as class or method accessor saves time when parsing. Traditionally the dot character is used when accessing a member of a class or object, for example: “myobject.mymethod(<params>);” is how you would call a procedure in nearly all languages. By altering this to “myobject:mymethod(<params>);” we are able to reserve the dot character for other things.

Second: Originally the combination of => and <= (arrow heads) was used to denote direction of data-flow inside the input() and output() blocks (handshake section). This turned out to be a bad idea because web-services and pure objects does not follow the concept of “consumer” or “producer”. A web-service contains both functions and procedures, and as such using data-flow arrowheads would require two registrations rather than one. Replacing the arrow-head symbolism with “@” (meaning “at”) makes more sense, since services and channels are also bi-directional.

The “@” at character is also used to denote “memory address of” (same as object-pascal) and “reference to”. For instance when passing an anonymous-procedure as a parameter you must prefix the in-place execute-block with “@”. See notes on anonymous procedures further down.

Third: To date the execute() program block allows you to define the variables visible to the block and shared between the module and the code inside the block. This has been poorly understood by commenters on this blog. The reason the execute() exists at all, as opposed to just typing the code in it’s place, is because a code block can execute in 4 different modes:

  • Blocking
  • Async
  • Threaded
  • Anonymous

An execute block is by definition anonymous since you cant apply a name to it as you would a method. However the data-scope (the variables visible to the block, or “known” by the block) can also be used when the block is threaded. This is why you have to define shared variables — or simply write use (*) as a parameter – which means “all”.

Linear execution

linear execution means that the block executes in the same thread as the main program. This means you actually dont have to define parameters, since all variables and objects of the program will be available to the block.

Execute (*) {
  stdio:writeln("linear execution");
}

Asyncronous execution

Async execution means that your code is executed in the same thread as the main program, but execution is done through interrupts, so that the program continues immediately and doesnt wait for the block to finish.

Execute async (stdio) {
  stdio:writeln("async execution");
}

Threaded execution

Threaded execution executes the block in it’s own process. Objects and variables you wish to share with the thread must be passed as parameters of the execute block. Note:under JavaScript this involves web-workers. nodeJS however supports real threads which is used by the code emitter.

Execute thread[Priority:Idle] (stdio,mySharedObject1,MySharedObject2)  {

  var msghandler = null;
  var myProcess = null;

  set msgHandler = open PIPE("messages");
  set myProcess = process("self");

  repeat(-1) {
    msghandler:waitfor(1000) {
      stdio:writeln("message recieved!");
    } fail (e) {
      criteria {
        myProcess:terminated = false;
      } execute (*) {
        /* Thread terminated, break free of wait loop */
        break;
      } else {
        /* thread not terminated, continue waiting
           for a message */
        continue;
      }
    }
  }

} finished {
  /* Code to execute when thread finishes */
}

The above code executes a code-block as a thread. The thread creates a pipe and waits for a message on the pipe (typically sent from the main program – but can also be triggered by another N++ application (or any application capable of creating and sending data over a pipe).

Anonymous procedures

Much like javascript N++ supports in-place anonymous procedures. Like other languages the declaration must have a compatible interface (parameters and their types):

Execute (*) {
  someclass:someproc( @execute(*){
      stdlib:writeLn("this execute block is anonymous");
      }, 100);
}

Hopefully this clears up the concept of execute-blocks and it’s parameters. The parameter list for the execute block simply defines what should be known for that block. This makes sense when you realize that the code-block can be executed as a completely separate thread.

Symbols so far

  • “:” Accessor, as in “object:method” or “interface:method”
  • “@” Reference to, address of, as in “myService @ process(‘name’);”
  • “<type | interface>” typecast as in, <ISomeInterface>SomeInstance:InterfaceMethod(Params);
  • “async” execute model, runs execute-block in async mode
  • “thread” execute model, runs execute-block as a separate thread
  • “[ attribute ]” accessor for attributes, arrays and dictionary elements. As in “object-factory[attribute-name:value, ..];”
    Also used as array accessors, as in “string name = Array[index]”
    Also used as dictionary accessors, as in object “temp = Dictionary[KEY];”

Attributes

N++ attributes are identical to what in other languages are called class-properties or class-methods, in that no instance needs to be constructed in order to change them. Attributes are typically used to initialize a class before you construct instances from it (an instance or “object” is what is created from a class, a class remains a blueprint or schema describing what you want to construct. This is classical OOP terminology).

* Note: The [ ] accessors are the same as those used for array access.

Under object pascal you would for instance write:

 type
    TMyClass = class
    strict private
    class var
       FRed: Integer;
       FGreen: Integer;
       FBlue: Integer;
     public
       class property Red: Integer read FRed write FRed;
       class property Green: Integer read FGreen write FGreen;
       class property Blue: Integer read FBlue write FBlue;
    end;

You would then initialize these values before you create an object from the class:

procedure TForm1.makeClass;
var
  mInstance: TMyClass;
Begin
  (* Initialize class level values *)
  TMyClass.red:=120;
  TMyClass.green:=19;
  TMyClass.blue:=22;

  (* construct instance from class.
     the values assigned to the class are mirrored on the instance,
     so the "red" property now contains 120 in our instance *)
  mInstance:=TMyClass.Create;
end;

Under N++ such special properties are called attributes and are not decorative like C# and C++ attributes. The word attribute and property (of something) means essentially the same thing; but under N++ we use them to differienciate between class and instance values. Attributes operate on the level of class (global), while properties operate on the level of instance (local).

Setting attributes

Attributes can be set directly or indirectly depending on the language construct. When obtaining service objects, which by nature are external processed without any constructors (external running programs, also running on other servers remotely) attributes function more or less as constructors:

    input {
      /* Consume WSDL Web-Service Endpoint */
      service1 @ service[URI:"http://www.test.com/SOAP/myService/WSDL/",
                 serviceType:SoapService];
    }

In the above example we set the attributes for the service we are going to use. The service can then be used through the identifier (service1). The “service” keyword is actually an object factory, which creates the invocation layer.

  /* create thread manually */
  var Thread myThread = new Thread
      [Priority:highest,name:"this is my thread"](*);

  /* set thread execution block */
  set @myThread:execute (*) {
  }
Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: