N++ execution model
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 (*) { }
Recent
The vatican vault
- January 2022
- October 2021
- March 2021
- November 2020
- September 2020
- July 2020
- June 2020
- April 2020
- March 2020
- February 2020
- January 2020
- November 2019
- October 2019
- September 2019
- August 2019
- July 2019
- June 2019
- May 2019
- April 2019
- March 2019
- February 2019
- January 2019
- December 2018
- November 2018
- October 2018
- September 2018
- August 2018
- July 2018
- June 2018
- May 2018
- April 2018
- March 2018
- February 2018
- January 2018
- December 2017
- November 2017
- October 2017
- August 2017
- July 2017
- June 2017
- May 2017
- April 2017
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- February 2013
- August 2012
- June 2012
- May 2012
- April 2012