o
odin.langpkg.dev
packages / library / rstil

rstil

fa19812library

Translatable Interpretable Language

MIT · updated 4 months ago

https://github.com/jtimon/til/raw/master/img/logo.svg

Translatable Interpretable Language

Til is a general purpose experimental language that is meant to be interpreted or translated to machine code or to some other high level language.

A priority is to have a self hosted implementation as soon as possible, although the bootstrap implementation is written in rust.

Motivation

There are too many languages, we need a new language that covers everyone’s use cases.

WARNING: Do not use this language, it is not ready.

This is just a pet experimental language. Nobody should use it for anything besides testing or extending it. It may never be finished. The name may change in the future too. It may become unmaintained.

WARNING Contributing

Until src/til.til does everything src/rstil.rs does and also compiles to a amd64-linux binary, it is not worth it.

Please, don’t even try yet.

Get started

See /src (except for src/rscil.rs) to read some examples.

To build the binary and run all tests (reading the Makefile may be instructive too):

make

The binary is currently “rscil” (built from rscil.rs), we will also have “til” (built from til.til), which will eventually replace rscil once it does everything rscil does plus is also able to translate itself to C and compile and link itself.

Dependencies

  • make (Optional, for convenience)
  • rlwrap (Optional, for the repl)
  • rustc (Temporary, needed until the language is self hosted)
  • gcc (for rstil build/run)
  • gcc-aarch64-linux-gnu (Optional, for linux-arm64 target)
  • gcc-riscv64-linux-gnu (Optional, for linux-riscv64 target)
  • mingw-w64 (Optional, for windows-x64 target)
  • qemu-user (Optional, for make test-cross)
  • wine (Optional, for make test-cross)

Milestones

  • [X] Interpreted
  • [X] Statically typed
  • [X] Error handling (more like roc than like rust/zig or go/odin/jai, let alone cpp, but more like java too)
  • [X] UFCS (Uniform Function Call Syntax) with method chaining
  • [X] Simulate pointers
  • [X] Self hosted arrays before being actually self hosted
  • [X] User defined variadic arguments
  • [X] tagged unions (aka algebraic types) - enums with Bool, I64, Str, struct, and enum payloads
  • [ ] Self hosted (initially only interpret itself, ie interpret til.til with til.til interpreted with rscil; ala pypy)
  • [ ] Compiled (initially through a C backend)
  • [ ] Can link C libraries
  • [ ] Truly self hosted, get rid of rust implementation and replace with bootstrap/til.c generated by til.til from itself.
    • The binary produced by “til interpret til.til compile til.til” must be identical every time if there are no changes.
    • It must be also identical to the one produced by “rscil interpret til.til compile til.til” before getting rid of rscil
  • [ ] Implement basic missing features
  • [ ] User defined modes
  • [ ] mode gui (using raylib), some of the core functions are not allowed in cli and viceversa
  • [ ] Allow different compilation targets
  • [ ] Allow modes to restrict targets
  • [ ] Implement operators
  • [ ] mode safe_script Only whitelisted commands (ls, mkdir) are allowed via run_cmd. See src/examples/hello_safe_script.til for usage example. TODO: user confirmation prompts instead of always failing for non-whitelisted commands TODO: current implementation can probably be bypassed with import
  • [ ] mode editor
    • [ ] Elisp backend for emacs
    • [ ] Lua backend for neovim
    • [ ] Vim backend for vim
  • [ ] mode web_lib Compiles to js or wasm, some of the core functions are not allowed in lib and viceversa
  • [ ] mode web_gui Compiles to js or wasm, some of the core functions are not allowed in gui and viceversa
  • [ ] mode web_cli this can simulate a console, for example to put the repl console in a simple html + js web
  • [ ] mode server kind of like a flask server in python, but as a mode
  • [ ] Introduce holyC backend
  • [ ] Support os=temple, try to do some minimal docker container server with it
  • [ ] Introduce backend MLIR (like mojo)
  • [ ]

Til’s eat your cake and have it too philosophy

  • Compiled and interpreted
  • Self hosted and backend agnostic
  • Purely functional and purely procedural
  • Strongly typed and a dynamic language (Dynamic special type for arguments that is not checked)
  • Using UFCS and calling it OOP is allowed
  • Semicolon agnostic (currently required only for external function declarations, but allowed after any statement)
  • Without “indentation-based block-level structure”, just braces for blocks
  • Flexible error handling styles (like in C, like in Go, like in Rust or in its own style similar to java: your functions, your choice)
  • General purpose and domain-specific with an extensible mode system

Early documentation

Pure functions:

So called “pure functional languages” do not use only pure functions, they all use procedures too, for side effects. At the same time, procedural languages can have pure functions too. Pure functions could be declared explicitly and the compiler could make sure that they are in fact pure functions.

This language has different reserved words for declaring different types of algorithms.

func

Pure functions (funcs) don’t have “side effects”. In other words, memoization is possible for funcs.

This is similar to using the keyword “pure” in D-lang functions.

proc

Procedures (procs) can have side effects. Procs can call funcs, but not the other way around. In other words, memoization may not be possible because the result for the same input may be different each time it is called or, even if it was the same, some side effects may not be executed if memoization was used instead of executing the procedure again.

macro

Macros are used for metaprogramming. Macros are executed at compile time. Macros can call funcs or procs and vice-versa, but after running the macros, the resulting program may still fail to compile.

Macros have more restrictions than func/procs in some senses but less restrictions in others.

For example, a macro can return the definition of a struct, or a body (a sequence of statements to be executed); whereas funcs and procs cannot.

At the same time, all the values of for the arguments of every call of a macro must be guaranteed to be knowable at compile time. The values of the arguments for a macro call must be literals or the results of other macros or of other functions whose arguments are in turn also literals or the result of other macros, or equivalent.

Modes:

Somewhat similar to roclang’s platforms, but they can be used per file rather than per executable, to impose special restrictions per file, as per the chosen mode. For example, there can be a “mode pure” that only allows pure functions to be defined in the file, but no procedures. Users should be able to define their own modes with their own extra restrictions and their own built-in types and core functions or procedures. A user could define a mode not allowing calls to core procedure “print”, or replacing it with his own version, for example.

Here are some “built-in” modes:

script

Basically no restrictions, statements will just be run as they are, including if, switch or while statements in the root context of the file, for example.

safe_script

Similar to script, but only whitelisted commands (ls, mkdir) are allowed via run_cmd. See src/examples/hello_safe_script.til for usage example.

TODO: user confirmation prompts instead of always failing for non-whitelisted commands. TODO: current implementation can probably be bypassed with import.

This idea comes from Roc, but as far as I know Roc hasn’t implemented it yet.

cli

It only allows declarations in the root context of the file. It requires the declaration of a proc named “main”, which will be run.

test

Similar to script, but it only allows declarations and calls in the root context. It does not allow mut declarations in the root context (this may change).

lib

Only allow constant declarations, not mut declarations. Useful for defining libraries that can be imported by other files.

liba

Like lib, but funcs can call print and println. Useful for debugging pure functions - if the func calls are constfolded, the prints will happen at compile time instead of runtime.

pure

Like lib, but it doesn’t allow proc declarations, calls or imports. When a mode pure file is imported, one can be sure that everything exposed by the file are constants or pure functions.

The subset of this language that’s allowed in mode pure is a truly purely functional language, more pure than lisp, clojure, haskell or erlang; in the “pure functions only” sense.

Not implemented yet.

pura

Like pure, but funcs can call print and println. Useful for debugging pure functions - if the func calls are constfolded, the prints will happen at compile time instead of runtime.