|Version 2 (modified by 14 years ago) ( diff ),|
An effort to design a high-level programming language for writing HelenOS severs and applications.
|Sysel Bootstrap Interpreter (SBI)||In progress||Interpreter of Sysel written in C. Runs in HelenOS and POSIX.|
|Sysel Compiler Toolkit (NNPS)||Not started||Modular compiler of Sysel written in Sysel itself. To produce C and/or LLVM IR.|
SBI is an interpreter of Sysel currently in development. It is available stand-alone for POSIX or integrated in HelenOS (only in Bazaar repository, not yet in a stable release). You can run it with the command "
sbi source_file.sy". Demos that you can run are available in
/src/sysel/demos. Source files comprising the library are in
You can also run
sbi without parameters to enter interactive mode.
Synopsis of current SBI features
- Primitive types: bool, char, int, string
- Compound types: class, multi-dimensional array
- Objective features: inheritance, grandfather class, static and non-static method invocation
- Syntactic sugar: variadic functions, accessor methods (named and indexed)
- Arithmetic: big integers, addition, subtraction, multiplication
- Static type checking (mostly), Exception handling
- Bindings: Text file I/O, WriteLine, Exec
Missing SBI features
- boolean operations (and, or, not)
- builtin object methods/properties
- static/nonstatic checking
- static class variables
- generic types
- method and operator overloading
Ideas for Sysel
It should be possible to use, with similar simplicity and the same level of static type checking, not only compulsory libraries, but also optional libraries and plugin libraries.
Compulsory libraries are those required every time the executable is invoked (equivalent to
gcc -lname). Optional libraries are only loaded once the application touches some symbol from the library. This is a very useful feature that allows building binaries with all optional dependencies enabled, yet the user need not install all these libraries if they do not want to. This helps avoiding dependency avalanches.
Plugin libraries are those where multiple libraries can exist written again some common plugin interface. One possibility is to have packages implement package interfaces. A package could be loaded at run time, a reference to it stored to a variable whose type is the package interface type. Then it would be possible to refer to symbols within the dynamic package using standard qualified names (e.g.
P.symbol). This enables full static type checking / interface checking for both the implementor and user of the plugin.
HelenOS IPC is usually employed in an RPC-like style. Remote objects would support asynchronous messaging in the language itself. Remote object classes (and interfaces) form a separate hierarchy of inheritance to the local classes and interfaces. Remote interfaces are equivalent to IPC interfaces now usually defined in HelenOS in
uspace/lib/c/include/ipc. They would naturally support (multiple) inheritance. Servers contain remote classes which implement these interfaces.
When a client wants to use some service, they are given a reference to a remote object. This reference identifies not only the server which we talk to, but possibly also the individual resource within the server that we are accessing. For a contrived example, a console server might provide the two interfaces:
interface IConsole, remote is fun GetVC(vc_index : int) : IVC; end interface IVC, remote is fun GotoXY(x, y : int); fun Write(s : string); end
When we invoke the GetVC() method, the console server will pass us a reference to the remote object implementing the requested VC. Then we can work with this particular VC using that reference:
var Con : IConsole; var VC : IVC; C = NameService.GetConnection("console") as IConsole; VC = C.GetVC(2);
Connection creation and termination, as well as transaction management (identifying the objects being worked with) is automatically handled by the language run-time. Also handled automatically is the creation of threads and fibrils within a server. A server can potentially handle any number of parallel requests (though it might be possible to limit this with some quota, if required). Concurrent access to remote objects is possible (and often desired).
When a method of a remote object is invoked, the method ID and its parameters are serialized and the resulting message is sent to the server. On the server the method ID and arguments are de-serialized and the implementation of the method is invoked. When the method returns, the return value (and possibly output arguments) are serialized and sent back to the client. At the client the return value(s) are de-serialized and returned to the caller.
- Multiple threads/fibrils may use the same remote object in parallel without fear of blocking each other (as long as the server is properly implemented)
- Stateful services can be implemented by the server handing out state objects (such as open-file object on a file server).
Promises can be used to express asynchronous behavior and potentially allow for promise pipelining (a form of optimization). In our case it would suffice to have a specialized form of promise, one that promises some data to be delivered from a remote object. Promises would be declared using a prefix type operator
As long as the data received from a remote object stays in a type that is
future, it is handled in an asynchronous fashion. Once the data is converted to a non-future type, the execution blocks until the data is received.
interface IAsyncIO is fun AReadBlock(addr : int) : future Block; end
fun ReadBlocksParallel(start_addr, count : int) : Block is var fblock : future Block; for i in range(0, count) do -- This does not block fblock[i] = AReadBlock(start_addr + i); end -- All reads are now being executed in parallel. -- Each array element is implicitly converted from future Block to Block. -- This blocks until all data has been received. return fblock; end