Common programming languages lack flexibility, which has led to the emergence of dozens of programming languages over the past decades. Those languages typically share a lot of common concepts and only differ in specific aspects. This situation burdens programmers with the necessity of learning multiple languages or using several languages even within a single project. Whenever a new technology emerges along with a new language, programmers need to learn this language from scratch. Most of this effort is wasted since much of the language repeats the same ideas found in its predecessors. Moreover, tool and library developers also need to support these new languages, resulting in significant wasted efforts from all parties involved and a significant increase in complexity and fragmentation in the software industry.
Alusus language solves this problem by making the language flexible and its features dynamically extensible during the compilation of the user's program, without the need to rebuild the compiler or make changes to its setup. This saves a lot of time and effort for both technology producers and users. Developers of new technologies will no longer need to create an entirely new language from scratch. Instead, they can simply create the new needed feature and deploy it as a library that can be imported into Alusus programs. These extension libraries have almost complete freedom to modify the language, such as adding new grammar rules or modifying code generation units of the compiler.
The flexibility of Alusus language allows it to be connected to multiple code generators. This means that different parts of the same program can be compiled or executed using different code generators. This idea is contrary to frameworks like LLVM, JVM, and .NET. For example, the LLVM framework provides a single executable code generation unit that is linked to front-end interfaces for multiple languages. Alusus, on the other hand, serves as a unified language interface that can be linked to multiple code generation backends based on the program's needs.
Integrating with different code generators can be done from within the code being compiled, or from a library imported by that code. This capability allows the programmer to keep their project unified within a single code base, and organize code parts based on their functionalities rather than the environment in which they will be executed, as we will see in later examples. It also enables the creation of more comprehensive libraries than what is possible in common languages. For example, a library can contain both the user interface code and the corresponding server code for a specific feature, all within a single library without the need to split it into separate server and user interface libraries.The following example illustrates the closure feature in Alusus language. Alusus compiler does not have built-in support for closures and does not understand the keyword `closure`. However, the library loaded at the beginning of the example adds this feature along with the required grammar rules. It can be observed in the example that the closure automatically distinguishes between different types of variables accessed within its body, and prepares the required payload accordingly. It includes the variable 'p' in the closure payload because it is a local variable, while it does not include the variable 'g' because it is a global variable. Similarly, it does not include the variable 'q' because it is not used within the function. This level of control in language extensions is impossible in common languages because it requires interoperability between the library and the compiler.
import "Srl/Console"; import "closure"; use Srl.Console; def g: Int; func getClosure (): (closure (Int)) { def p: Int = 2; def q: Int = 4; return closure (a: Int) { print("closure arg: %d\n", a); print("closure payload var: %d\n", p); print("global var: %d\n", g); }; } def c: closure(Int) = getClosure(); g = 1; c(3); // Output: // closure arg: 3 // closure payload var: 2 // global var: 1
The following example illustrates adding new features to facilitate database integration and writing query statements. In the example, it can be observed that it starts with importing the package manager, which is used to download and import a library directly from the web (in this case, from the GitHub repository). This library, in turn, adds new features to match model classes with their corresponding tables in the database and provides grammar rules for writing conditions in query statements.
In the `addCar` function, you can see that the user does not need to write SQL statements to add a new record. Instead, the library automatically deduces them from the modifiers that decorates the class and its attributes. In the `findCars` function, the search condition is written in Alusus language itself, as if you were writing an expression in a conditional statement. The compiler in turn translates this condition into an SQL where clause.
import "Apm"; Apm.importFile("Alusus/Rows", { "Rows.alusus", "Drivers/Postgresql.alusus" }); use Srl; use Rows; @model["cars"] class Car { define_model_essentials[]; @notNull @primaryKey @Uuid @column def id: String; @VarChar["50"] @column def name: String; @Float @column["car_price"] def price: Float; } def db: Db(PostgresqlDriver(ConnectionParams().{ dbName = "alusus"; userName = "alusus"; password = "alusus"; host = "0.0.0.0"; port = 5432; })); if !db.isConnected() { System.fail(1, String("Error connecting to DB: ") + db.getLastError()); } db.schemaBuilder[Car].create(); function addCar(name: String, price: Float) { def c: Car; c.id = generateUuid(); c.name = name; c.price = price; db.save[Car](c); } function findCars(maxPrice: Float): Possible[Array[SrdRef[Car]]] { return db.from[Car].where[price <= maxPrice].select(); }
The following example illustrates writing a full stack web app that contains both server-side and user interface code within a single file. The example starts by using the package manager to load the Web Platform library. Web Platform distinguishes backend endpoint functions using the `@beEndpoint` modifier and automatically integrates their calls into the web server. Functions decorated with `@uiEndpoint` modifiers are compiled by WebPlatform into WebAssembly and are served on the web server at the route mentioned in the modifier's parameter. In this example `postMessage` and `getMessages` are added as endpoints served at `/messages` route, whereas the `main` function is translated into WebAssembly and served at the `/` route. In the future calls to backend functions will be automatically translated into HTTP requests.
import "Build"; import "Apm"; Apm.importFile("Alusus/WebPlatform"); use Srl; use WebPlatform; //============================================================================== // Backend def MAX_MESSAGES: 12; def messages: Array[String]; @beEndpoint["POST", "/messages"] func postMessage (conn: ptr[Http.Connection]) { def postData: array[Char, 1024]; def postDataSize: Int = Http.read(conn, postData~ptr, postData~size); if messages.getLength() >= MAX_MESSAGES messages.remove(0); messages.add(String(postData~ptr, postDataSize)); Http.print(conn, "HTTP/1.1 200 Ok\r\n\r\n"); } @beEndpoint["GET", "/messages"] func getMessages (conn: ptr[Http.Connection]) { def response: String = String.merge(messages, "<br>"); Http.print(conn, "HTTP/1.1 200 Ok\r\n"); Http.print(conn, "Content-Type: text/plain\r\n"); Http.print(conn, "Cache-Control: no-cache\r\n"); Http.print(conn, "Content-Length: %d\r\n\r\n", response.getLength()); Http.print(conn, response.buf); } //============================================================================== // Frontend Pages @uiEndpoint["/"] @title["WebPlatform Example - Chat"] func main { def onFetch: closure (json: Json); Window.instance.setView(Box({}).{ style.{ height = Length.percent(100); justify = Justify.SPACE_BETWEEN; display = Display.FLEX; layout = Layout.COLUMN; }; addChildren({ Header(), Box({}).{ style.{ width = Length.percent(100) - Length.pt(10); padding = Length4.pt(5); display = Display.FLEX; layout = Layout.COLUMN; flex = Flex(1); }; addChildren({ Text(String()).{ style.{ width = Length.percent(100); height = Length.percent(100); fontColor = Color(50, 50, 50); fontSize = Length.pt(20.0); }; onFetch = closure (json: Json) { def status: Int = json .getObject("eventData") .getInt("status"); if status >= 200 and status < 300 { def data: String = json .getObject("eventData") .getString("body"); if this.getText() != data { this.setText(data); } } else { // TODO: Notify user. } }; } }); }, TextEntry().{ width = Length.percent(100) - Length.pt(3); height = Length.pt(50); onNewEntry = closure (newData: String) { sendRequest( "POST", "/messages", "Content-Type: application/text", newData, 10000, closure (Json) {} ); sendRequest("GET", "/messages", null, null, 500, onFetch); }; } }) }); startTimer(500000, closure (json: Json) { sendRequest("GET", "/messages", null, null, 500, onFetch); }); runEventLoop(); } //============================================================================== // Project Control Console.print("Starting server on port 8010...\nURL: http://localhost:8010/\n"); runServer({ "listening_ports", "8010", "static_file_max_age", "0" });
There are no limits to what libraries can do, and there are no limits to the number or type of libraries that can be loaded at any time. Alusus language imposes no restrictions on the number of grammar rules that can be added, and it provides features to minimize the possibility of conflicts between grammar rules. For more information, please refer to the language design in the documentation page.