Solveig

Solveig

A small object-oriented language, its bytecode virtual machine, and a REPL — written in C11, with no dependencies beyond a compiler and make.

Everything is an object, including classes, and all work happens by sending messages to them.

a := #45.        ; ':=' binds a name; '#' tags an integer ( bare 45 is a float )
a:print.         ; -- ':' sends a message, '.' ends the statement

xs := [#1, #2, #3].
xs:do({ x | x:print }).        ; -- { } is a block: code as a value

There is no control-flow syntax at all. ifTrue, ifElse, and whileTrue are ordinary messages that take unevaluated blocks, which is enough to be Turing-complete:

integer:factorial := {
    self:lessThan(#2):ifElse({ #1 }, { self:mul( self:sub(#1):factorial ) })
}.
#20:factorial:print.      ; #2432902008176640000

Written literally those compile to jumps — no block allocated, no frame entered — while staying ordinary messages you can send any other way.


Start here

Also: Design for the object model and the .sob format; The instruction set for every opcode SolVM executes; Fetching a method for holding a method as a value; One hierarchy for how a method on object reaches a number, and where a value is still not an object; Choosing what to do for switch statements, jump tables and the traps in them; Absence for nil, empty and unset; The class side and the instance side for the last design question and how it was closed; SolaBasic for the compiled BASIC that targets this bytecode, and where its subset stops, with a reference manual for writing it; Making a release for the four files, the compatibility check and the two fixups a release page needs; Ideas considered for what was weighed and what was turned down; Roadmap for what is left; Completed for the case behind each piece of work that is done; and Changelog for what has changed.

The names

Solveig is the project. The language it holds is Solum, compiled by Solas, run by SolVM, and explored through Solis.

Solveig is Old Norse — Sólveig, from sól, “sun”, joined to veig, usually read as “strength”. Most people who recognise it will recognise it from Ibsen’s Peer Gynt, where Solveig is the one who waits.

SolVM reads two ways, and both are meant. It is the Sol virtual machine — and it is SOLVM, which is how solum was written before the alphabet split V into two letters. Classical Latin had a single V for both the vowel and the consonant, which is why Roman inscriptions give SOLVM and not SOLUM. So the machine is not named after the language it runs. It is the same word, cut in stone.

Solum was picked for both of its Latin senses, and each of them says something about the language.

As a noun it is the ground — soil, floor, the base a thing stands on. That is what the machine is to the language it runs, and what the language is to anything written in it.

As an adverb, sōlum, it means “only”, and that is the design principle rather than a decoration. There is only one kind of thing here: everything is an object, classes included. There is only one thing that happens to it: a message is sent. No operators, no control-flow syntax, no second mechanism behind the first. Only is the whole idea.

Those are two different words, and the sun is a third. Latin tells them apart by vowel length — solum the ground has a short o, sōlum “only” has a long ō from sōlus, “alone”, and sōl, sōlis, the sun, is a root of its own. They are not one word wearing three hats. They are three words that happen to look alike, chosen for what each of them says.

The sun is what ties them together. It is the single body everything else in the system turns around, and the one thing this planet’s life has always depended on — alone in the sky, central, and the reason anything else works. A language in which there is only one kind of thing, and only one thing you can do to it, is named after that on purpose. Solis is “of the sun”. And Solveig carries it into Norse: sól joined to veig, the strength of the sun — the same star, in a different language.

Status

0.43.0 — the language answers 145 messages, up from 144, and .sob files are still format version 14. gzip is the twenty-second program and inflates a gzip stream, held against the tool that produced every input it is checked against — 66 round trips, byte for byte. It was written to measure what a 32 KB window costs as boxed values and found the window is 4.8% of the program where reading the bits is 70.7%. system:readUpTo(#n) answers up to n bytes of standard input exactly as they were sent, which is what lets a program’s memory stop depending on the size of its input.

Working: the scanner, the single-pass compiler, the re-entrant dispatch loop with call frames, blocks with lexical capture, message-based control flow, a mark-sweep collector over objects, blocks and compiled code, and the .sob format with its verifier.

The language is Turing-complete, does not leak, and has strings, arrays, dictionaries, symbols, user-defined objects, reflection, sorting, formatted output, and conversions between every pair of types that has an unambiguous one. A program reads and writes files, reads its input, times itself, stops with a status, and is split across files with @include.

Around it: a debugger, @expr for infix arithmetic where the notation is worth it, extensions loaded from a C binary at run time, and a host API for embedding the machine. Measured against CPython 3.14 on nine matched programs it comes out a little ahead.

It is 0.1 rather than 1.0 because the restrictions in the roadmap are deliberate and documented: no non-local return, a capturing block tied to its frame, recursion to about 254 levels, and text is bytes.

Arithmetic is strict throughout: integers and floats never coerce, and integer overflow traps rather than wrapping.

make          # builds bin/solas, bin/solvm, bin/solis
make test     # builds and runs the test suite
./bin/solas examples/hello.sol      # writes examples/hello.sob
./bin/solvm examples/hello.sob
./bin/solis                         # a prompt; input may span lines

.sob files are little-endian and portable, and are verified before they run.

Examples and programs

Two directories, and the split is what each was written for.

programs/ — twenty-two whole programs, each written to do a job and using whatever the language turned out to have. They are where nearly every roadmap entry after the first dozen came from: somebody wrote one and found out what it wanted. programs.md says what each does, how to run it, and what it found.

   
log reads an access log and reports on it
evaluator tokenises, parses and folds an expression
manifest reads a JSON file, queries it, writes it back
page reads an HTML file and reports on it
mirror copies one directory tree into another
tools does its job by running other programs
serve answers one HTTP request, without being injected
disasm reads a .sob file and says what is in it
expect checks every example against its own comments
bench times a command, and says whether two really differ
basic runs a BASIC listing — an interpreter for ECMA-55
edit edits a file on the screen, in the manner of vi

Two of them run on lib/json.sol and lib/html.sol — a JSON reader and an HTML reader written in Solum. The last one is an interpreter for another language, and is where the mathematics in 0.32.0 came from.

examples/ — thirty-five demonstrations, one for every concept the guide names: hello, binding, stock, numbers, values, blocks, methods, objects, arrays, strings, symbols, format, reflect, strictness, library, include, system, reading, files, dictionaries, loops, errors, walk, time, keys, random, scanning, commands, matching.