SLUL specification ================== Copyright © 2026 Samuel Lidén Borell SPDX-License-Identifier: EUPL-1.2+ OR LGPL-2.1-or-later 1. Introduction =============== 1.1. Rationale -------------- The SLUL language is meant to be: * Trustworthy from a user point of view; code should always have the minimum necessary privileges and it should be memory safe without any loopholes. * As free as possible from recurring maintenance burdens; in particular, changes in dependencies should not necessite additional tasks for the dependent modules (such as rebuilds or code adaptations). * Have a low threshold for use; it should not require a large runtime (technical threshold) or learning lots of new concepts or terminology (knowledge threshold). This document defines the SLUL programming language and its execution environment. 1.2. Organisation of the Document --------------------------------- This document is structured in these parts: 1. This introduction 2. A high-level architecture overview 3. Language Syntax and Semantics 4. Module System 5. Runtime Library 6. Subsets and Extensions 7. Binary Interface Cross-references are given in parentheses, for example (1.2) would refer to this section. 1.3. Definitions ---------------- * Application * Execute * Giveme * Giveme Section * I/O * Library * Module * Runtime Loader * Top-Level 2. Architecture Overview ======================== 2.1. Modules ------------ A module is the highest unit for organising software. It can contain a number of top-level definitions (3.2; 3.3) such as classes. A module can either be an application module (2.1.1) or a library module (2.1.2). 2.1.1. Application Modules -------------------------- An application module must define at least one class that is a service (x.x.x). Each service provides one or more entry points (x.x.x) for the application. A such entry point can be called upon some environment-defined event, such as when the application is started, (in graphical applications) when a button is pressed, (in web applications) when a page is loaded, for example. Service classes may contain one `giveme` section (x.x.x) for dependency injection of, for example, I/O or parameters provided by the runtime. 2.1.2. Library Modules ---------------------- A library module defines a interface that may be used from other modules (applications and as well as other libraries). An interface contains a set of exported classes and functions, that are defined in the interface file (x.x.x) of the library. Libraries can (and should) be versioned (x.x.x), and definitions may be tied to a specific versions. Versions and their definitions are immutable once released. From the definitions contained in given version, a so called "API hash" can be computed (x.x.x). 2.2. The Parts Needed to Load an Application -------------------------------------------- The following parts are necessary to load an application: * The application module (2.1.1). * Any library modules (2.1.2) needed by the program. * The runtime loader, which loads the main program as well as the library modules. The runtime loader also processes the `giveme` section (x.x.x) of each service class. 2.2. Source Files in a Module ----------------------------- A SLUL module consists of the following files: * The module source code (`.slul` files, except `interface.slul`) * For library modules, the interface definition file (`interface.slul`) * For library modules, an API index file (`api.index`) * A list of dependencies (`deps.index` file) * Optionally, a list of all `.slul` and `.index` files. Here is an example list of files in an application module: deps.index SomeCommand.slul Here is an example list of files in a library module: deps.index api.index sources.index interface.slul SomeThing.slul Section (11.1) contains a list of restrictions on filenames. 2.3. Interface Directories -------------------------- In order to use a module as a dependency, it must have been copied into an interface directory with the filename corresponding to the module name, plus the `.slul` file extension. For interfaces contained unversioned definitions, the filename must instead have the format `modulename-apihash.slul`, where the API hash (x.x.x) is computed over all unversioned definitions with the preceeding version set to the last defined version (if any). The compiler looks for interfaces in the following order. Not all directories are applicable to all operating systems or environments: * Interface directories specified with compiler command-line options (10.1). * Per-user interface directories (x.x.x). * Local interface directories (x.x.x). * System interface directories (x.x.x). 3. Language Syntax ================== 3.1. Valid Lexical Tokens ------------------------- TODO 3.2. Anatomy of a Module Source File ------------------------------------ A module source file whose filename begins with an uppercase letter is a source is a class file. The contents in a class file is considered to be part of a class (x.x.x) with the same name as the file, minus the directories and file extension. A module source file whose filename begins with a lowercase letter is called a utility file. In a utility file, definitions do not belong to any class. A class file may contain the following top-level definitions, in the following order: 1. A service type definition (3.4.1) or a class definition qualifier (3.4.7.1) 2. A `giveme` section (3.4.2) 3. Any number of fields (3.4.3) 4. Any number of constructors (3.4.4) 5. Any number of functions (3.4.5) Top-level definitions may not be indented. 3.3. Anatomy of an Interface File --------------------------------- A library module must have an interface file, that describes the exported definitions. Exported definitions are made available to be used by other modules. TODO require interface files to begin with `library `? An interface file may contain the following top-level definitions, in the following order: 1. Optionally a `usetype` section (3.4.6) 2. Any number of constants (3.4.3.1) 3. Any number of class definitions (3.4.7) The classes may in turn contain the following top-level definitions, again in the following order: 1. Any number of fields (3.4.3) 2. Any number of constructors (3.4.4) 3. Any number of functions (3.4.5) Note that unlike module source files, interface files do not contain `end` keywords, since top-level definitions automatically end where the next top-level definition begins. Top-level definitions may not be indented. 3.4. Top-Level Definitions -------------------------- 3.4.1. Service Type Definitions 3.4.2. `giveme` Sections 3.4.3. Field Definitions 3.4.3.1. Constant Definitions 3.4.4. Constructor Definitions 3.4.5. Function Definitions 3.4.6. `usetype` Sections when an interface requests a type in `usetype` then any module, that depends on the interface, must also depend on the module that defines the type. 3.4.7. Class Definitions 3.4.7.1. Class Definition Qualifiers 3.5. Identifier References -------------------------- There are two types of identifiers: types, which start with an uppercase letter and function/data identifiers, which start with a lowercase letter. Identifiers can be defined and referenced in any order, except for local variables which have to be defined before use (x.x.x). Identifiers can only be defined once, and may not "shadow" another identifier in an outer scope. However, identifiers can exist with the same name if they are in different scopes that are not visible in either of the other scope. Each module has it's own namespace (x.x.x), and depending on a module only makes identifiers in the requested version visible, and they only become visible in the module/interface that requested them (x.x.x). See also `usetype` (3.4.6). 3.6. Type References -------------------- TODO Examples of type references Int List Int Int i List Int l Map String Int m Map String List Int ml List Map String Int lm GenericType1 (GenericType2 Int) String g TODO problems: * Make visual separation clearer between the type and what follows after it. - use double spaces? Int i List Int l Map String Int m Map String List Int ml List Map String Int lm GenericType1 (GenericType2 Int) String g - use alignment? Int i List Int l Map String Int m Map String List Int ml List Map String Int lm GenericType1 (GenericType2 Int) String g - use parentheses? but perhaps harder to parse? (Int) i (List Int) l (Map String Int) m (Map String List Int) ml (List Map String Int) lm (GenericType1 (GenericType2 Int) String) g - use a terminator character/sequence? Int: i List Int: l Map String Int: m Map String List Int: ml List Map String Int: lm GenericType1 (GenericType2 Int) String: g - use brackets/parantheses around the variable identifier? Int [i] List Int [l] Map String Int [m] Map String List Int [ml] List Map String Int [lm] GenericType1 (GenericType2 Int) String [g] - allow writing generic types together(!): Int i ListInt l MapStringInt m MapStringListInt ml ListMapStringInt lm GenericType1(GenericType2Int)String g - Use [] or <> like other proglangs (but should there be a comma?)? Int i List l Map m Map> ml List lm GenericType1,String> g - Use [] between the base type and the type parameters? (see `notes/combined_list_and_array.txt`). And use concrete maps? (see next section) Int i List[]Int l MapOfString[]Int m MapOfString[]List[]Int ml List[]MapOfString[]Int lm GenericType1[](GenericType2[]Int)[]String g * How to call methods of objects of the parameter type? - Add a possibility to require a specific type. - Can it be done without a vtable? Maybe pass some info along with the generic this/object parameters? - Or don't do that at all, and have StringMap, IntMap, etc. (which could have a more efficient encoding/lookup). - But `StringMap Int` is "mixed endian". - `MapOfString Int` ? - `MapFromString Int` ? - `MapFromStringTo Int` ? - `MapStringTo Int` ? - `FromStringTo Int` ? - `LookupWithString Int` ? - `LookupFromString Int` ? - Or, even reverse the order? `Int StringMap` - `Map_String Int` ? - `LookupS`, `LookupI` - Or avoid the problem: - `NameMap`, `NumberMap`, `CustomMap` - `NameLookup`, `NumberLookup`, `CustomLookup` - there could also be `AsciiCaseInsensLookup` for example. (but there are many combinations!) - Or have "simple objects" that cannot have outgoing pointers, and have a unique representation of each value, and hence only need length in order to compare them? * TODO 3.7. Function Code ------------------ * TODO 4. Module System ================ * TODO - describe the module system - describe interface files - describe API hash computation * TODO language semantics section (section 5?) * TODO 5. Semantic Checks and Program Behaviour ======================================== * TODO 6. Runtime Library ================== * TODO 7. Subsets and Extensions ========================= * TODO - how to "invoke" a subset or extension in a module - the bootstrapping subset - extensions (which ones should be extensions, and which ones should be built-in?) - int64? - integer wrap-around? - floating point (different formats, could be several extensions) - vector? 8. Repositories =============== x.x.x. Local Repositories ------------------------- TODO /usr/share/slul-interfaces, /usr/local, ~/.local, %COMMONFILES%, ... 9. Binary Interface =================== * TODO - general considerations / rationale - common definitions - SYS-V based interface - Win32/64 based interface? - WASI based interface? 10. Compiler Command-Line Syntax =============================== TODO 11. Details =========== 11.1. Restrictions on Filenames ------------------------------- The purpose of these rules is to support a wide variety of file systems. Filenames are case-sensitive and must use only English letters a-z (uppercase and lowercase), digits 0-9 and underscores "_", followed by a dot "." and a lowercase file extension. The first character in a filename must always be a letter (not a digit, dot or underscore). Each file or directory must have a filename that is case-insensitive distinct from all other filenames. No files may have any of the following names, case-insensitive, before the file extension: * `aux` * `com` followed by a digit 0-9 * `con` * `lpt` followed by a digit 0-9 * `lst` * `nul` * `prn` The total length of a filename, including directory names and a 1 character separator after each directory, may not exceed 100 characters. 11.2. API hash computation -------------------------- TODO