= Sysel = An effort to design a high-level programming language for writing HelenOS severs and applications. * [https://launchpad.net/sysel Sysel project at launchpad] * [http://trac.helenos.org/trac.fcgi/browser/head/uspace/dist/src/sysel/demos Examples of real (working) Sysel code] Note that Sysel syntax is not finalized yet so the examples presented may be subject to change. == Roadmap == || '''Sub-project name''' || '''Status''' || '''Description''' || || 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 == 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 `/src/sysel/lib`. 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 properties), autoboxing * Arithmetic: big integers, addition, subtraction, multiplication * Static type checking (mostly), generic classes (unconstrained), exception handling * Bindings: Text file I/O, `WriteLine`, `Exec` === Missing SBI features === * division * boolean operations (and, or, not) * structs * interfaces * delegates * builtin object methods/properties * static/nonstatic checking * static class variables * working with binary data * generic type constraints * method and operator overloading * packaging == Ideas for Sysel == === Dynamic linking === 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. === Remote objects === ==== Basics ==== 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); VC.GotoXY(10, 10); VC.Write("Hello World!"); }}} 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). ==== Remote invocation ==== 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. Some notes: * 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 ==== [http://en.wikipedia.org/wiki/Futures_and_promises Promises] can be used to express asynchronous behavior and potentially allow for [http://en.wikipedia.org/wiki/Promise_pipelining#Promise_pipelining 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 `future`. 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. Example: {{{ 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 }}} === String language specification === It has been suggested by Pavel Rimsky that very often string literals in a program contain data in some machine readable language (e.g. format strings, SQL statements) or references to external resources. It might be useful to be able to somehow specify this in the program, so that external tools could recognize and work with these for purposes such as syntax checking, refactoring, etc. === Miscellaneous ideas === * Member pointers