Projects › R#

R#

My own programming language. It reads like Python and compiles through C to a native executable.

R# (Rosary Sharp) is a programming language I’m building from scratch. The idea is simple: write code that feels like Python, and get a program that runs at C speed. The compiler is written in C++ and turns .rsh files into C, then a normal C compiler turns that into an .exe. No interpreter and no garbage collector.

It’s early. The language spec is finished and the lexer is being written now, so none of the code below compiles yet. This is what it’s being built to run.

fn fib(n: int) -> int:
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

fn main():
    for i in 0..10:
        print("fib({i}) = {fib(i)}")

What writing it feels like

  • Python-style blocks: a colon and four spaces. No semicolons, no braces, no header files.
  • Static types without the typing: function signatures are written out, local variables work out their own type. let can’t change, var can.
  • Strings that format themselves: "{pi:.2f}" uses Python’s format specs, and they’re checked when you compile, so .2f on a string is an error before the program ever runs.
  • Lists and maps built in: list[int], map[string, int], slices, for k, v in my_map.
  • No exceptions: things that can fail return Result or Option, match takes them apart, and ? passes an error up.
  • Structs, enums and methods: methods in impl blocks compile down to plain C functions, so calling one costs nothing extra.
enum Shape:
    Circle(radius: float)
    Rect(w: float, h: float)

fn area(s: Shape) -> float:
    match s:
        Circle(r):  return 3.14159 * r * r
        Rect(w, h): return w * h

fn main():
    let shapes = [Circle(2.0), Rect(3.0, 4.0)]
    for s in shapes:
        print("area = {area(s):.1f}")

Memory

Numbers are copied. Strings, lists, maps and structs are reference counted: passing one around shares it, and it’s freed the moment nothing uses it anymore. That makes cleanup predictable, with no garbage collector pausing the program. .copy() makes a real copy when you want one. The catch is that objects pointing at each other in a loop never get freed, which is a known limit of v0.1.

Calling C

Any C library is one line away. Declare the function and call it:

import c "math.h"

extern fn sqrt(x: f64) -> f64

fn main():
    print("sqrt 2 = {sqrt(2.0)}")

Anything that touches raw pointers has to go inside an unsafe: block.

How it compiles

.rsh  →  lexer  →  parser  →  type checker
      →  C11 code + small runtime
      →  clang / gcc / zig cc  →  native .exe

The generated C keeps #line markers, so if the C compiler or a debugger complains, it points at the right line in the .rsh file and not the generated C. R#’s own errors show the file, the line and a caret under the problem, like Rust’s.

Roadmap

  • M0 The language spec
  • M1 Lexer (in progress)
  • M2 Parser
  • M3 Type checker
  • M4 C backend and runtime: hello world, fib and structs run natively
  • M5 Collections, strings and Result
  • M6 rsharp build and rsharp run
  • M7 VS Code syntax highlighting
  • M8 Language server: errors, hover and go-to-definition in the editor
  • M9 Python libraries from R#, by embedding Python

Classes, user-made generics, async and threads are left out of the first version on purpose, so it stays small enough for one person to finish.

How this page is styled (Source theme)
  • The page is styled like R# open in an editor: near-black, square corners like the logo's frame, one purple accent, and Cascadia Code (Consolas or Liberation Mono where it isn't installed) wherever code would be.
  • The code is really highlighted. I wrote a small TextMate grammar for R# from the language spec, the same kind of file VS Code uses, and Shiki colours the code with it when the site is built. The page ships plain coloured HTML and no highlighting script. It's also a first draft of the VS Code highlighting on the roadmap.
  • Code blocks are editor panes. ```rsh title="fib.rsh" in the Markdown puts the file name on the block, and it's shown as a tab with content: attr(data-title). Line numbers come from a CSS counter() on each line.
  • The title is coloured like the logo: the R purple, the # white. It's one linear-gradient clipped to the text with a hard stop at 1ch. That works because in a monospace font every character is exactly 1ch wide. Section headings are written like R# comments.
  • The roadmap is a Markdown checklist. The first unticked milestone (the one I'm working on) is found with :has(:checked) + :not(:has(:checked)) and blinks like the cursor.
  • Hover the header picture to run a program: a prompt types rsharp run hello.rsh one character per steps() frame, then the output appears.