Xag

Dot-Chained SyntaxUnusual & Verbose SyntaxSafeSteep Learning Curve & Could Reject Working ProgramsExcellent Runtime PerformanceSlow Compilation Time

Under development, don't treat this website's info as truth.

A programming language built around high performance, helpful error messages, and Rust-style memory management. There is no int on its own, because there is no size to assume. Programs are compiled ahead of time, either to native code or to a form run by an AOT interpreter.

Questions

I've seen languages with only positives. No negatives.

They most likely have flaws, but weren't written down.

To be honest too, we didn't write down all flaws of Xag, but we did write down the most major. At least we wrote them down. We don't want you to regret our language.

Reading a program

Items sit next to each other and are used in order. Nothing is concatenated into a third thing, so there is no + for text.

# Chains, defaults, and a loop.

const.int64 'LIMIT' = [*10*];

fn.int64 'sum-to' [int64 'n'] {
    var.mut.int64 'total' = [*0*];
    loop.range.int64 'i' = [*1*, 'n'] {
        set 'total' = ['total' + 'i'];
    }
    give ['total'];
}

START {
    var.int64 's' = [sum-to['LIMIT']];
    print.stdout[str:*sum to * 'LIMIT' str:* = * 's' \n];
}
counting.xagxagc run counting.xag
sum to 10 = 55

What a name owns, and what it lends

Ownership is Rust's, checked when the program is compiled, with no garbage collector under it. A transfer is spelled at the call site, so a reader never has to work out from a function's signature that a value has left.

# Lending for reading, lending for writing, and handing over for good.

fn.int64 'size' [ref.str 'text'] {
    give [count['text']];
}

fn.nothing 'shout' [refmut.str 'text'] {
    set 'text' = ['text' *!*];
}

fn.nothing 'keep' [str 'text'] {
    print.stdout[str:*kept: * 'text' \n];
}

# Two things are lent and one comes back, so the loan is named.
fn.ref.'life'.str 'longer' [ref.'life'.str 'a', ref.'life'.str 'b'] {
    if count['a'] >== count['b'] {
        give ['a'];
    } else {
        give ['b'];
    }
}

START {
    var.mut.str 'greeting' = [*hello*];
    var.str 'reply' = [*hi*];

    # Lent for reading. 'greeting' still holds what it held.
    print.stdout[str:*size: * size[ref 'greeting'] \n];

    # Lent for writing, which is why the chain says `mut`.
    shout[refmut 'greeting'];
    print.stdout[str:*after: * 'greeting' \n];

    # The answer is borrowed from whichever of the two was longer.
    var.ref.str 'winner' = [longer[ref 'greeting', ref 'reply']];
    print.stdout[str:*longer: * 'winner' \n];

    # Handed over for good. 'spare' holds nothing after this line.
    var.str 'spare' = [*spare*];
    keep[move 'spare'];
}
borrowing.xagxagc run borrowing.xag
size: 5
after: hello!
longer: hello!
kept: spare

When it has something to say

Take the program above, hand a value over for good, and then ask for it again. This is what comes back — not a paraphrase of it:

Hello, I think there may be thing(s) wrong with your code. I'm sorry, if I'm wrong.

file: moved.xag, line: 11, column: 18 (moved.xag:11:18)

`'greeting'` was moved, and holds nothing now.

  11 |     print.stdout['greeting' \n];
     |                  ^^^^^^^^^^ used here
  10 |     keep[move 'greeting'];
     |          ^^^^^^^^^^^^^^^ but it was handed over here

Error code: E0403
Rule(s) broken: a name holds its value until it is moved, and then holds nothing
Tip(s): what was moved is somewhere else now, and there is only ever one of it.

1 error.
If I am wrong about any of that, please tell me: https://github.com/Artificial-IntelligenceAI/Xag-lang/issues

A diagnostic that points at the wrong thing, or names a rule the program did not break, is a bug of the same kind as miscompiling — the compiler is telling the reader something untrue either way.

Building it

The compiler is written in C++20 against LLVM's native C++ API. It needs LLVM 23 or newer, CMake and Ninja.

git clone https://github.com/Artificial-IntelligenceAI/Xag-lang.git
cd Xag-lang
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
ninja -C build
./build/xagc run examples/counting.xag

xagc run runs a program on the test interpreter, xagc fast on the fast one, and xagc build compiles it ahead of time and writes an executable beside it. xagc check checks a program without running it.

There is no install yet. Building from source is the way to run it.