182 lines
6.2 KiB
Markdown
182 lines
6.2 KiB
Markdown
# Q&
|
|
|
|
## About
|
|
|
|
A single-pass byte code interpreter with the ability to pre-compile executables
|
|
into a format that can be efficiently executed by the runtime.
|
|
It serves many features, which are listed a bit further down below.
|
|
|
|
This would have officially been the fourth iteration of my language (sqript4),
|
|
but I thought I'd stop with the numbers as people would ask what happened to the
|
|
previous three languages.
|
|
|
|
Q& (or Qamp for Q Ampersand) was originally made for my contributions to the
|
|
Advent Of Code 2025, but I thought it's a nice and stable enough concept to be
|
|
sharing it to the public anyway.
|
|
|
|
## Usage
|
|
|
|
To build **Q&**, you need nothing more than a C# compiler that supports .NET10.
|
|
There are .NET8 compatible versions available, but they are not recommended,
|
|
as **Q&** utilizes all of the performance optimizations .NET10 has to offer.
|
|
|
|
After cloning, a simple `dotnet build ./` in the root directory will suffice.
|
|
|
|
You can execute pre-compiled .sqi files using `qamp.runtime.exe <filepath> [parameters]`,
|
|
or the REPL/CLI using `qamp.cli.exe [parameters]`.
|
|
If you want to build your own source into pre-compiled instruction files,
|
|
use `qamp.digest.exe <dirpath>` - the digester will look for an `Init()` function
|
|
declared in any of the files, and build the instructions from there.
|
|
|
|
## Language & Syntax
|
|
|
|
You are free to choose between the classic sqript-style dialect `*~ q <~ 0xf;` or
|
|
the standard dialect, which is strongly inspired by the C# standard: `var q = 0xf;`.
|
|
|
|
All of the following examples are written in the classic dialect `--dialect=sqr` or `-d=q`.
|
|
|
|
### Syntax
|
|
|
|
Most basic Usage:
|
|
```cs
|
|
# this is a comment.
|
|
*~ q <~ 12; # q == 12
|
|
*~ f <~ (n) <: n < 2 ? n : f(n-1) + f(n-2); # fibonacci
|
|
f(12); # 233
|
|
*~ a <~ [1, 2, 3]; # a = [1, 2, 3]
|
|
*~ x <~ a:0 + a:2; # x == 4
|
|
```
|
|
|
|
### Assignments
|
|
|
|
**Q&** has two distinct ways to assign a value to a variable:
|
|
- By reference `<&`, or
|
|
- By value `<=`.
|
|
|
|
Using the auto-assignment operator `<~`, **Q&** will choose the correct assignment based on what
|
|
type the right-hand expression consists of.
|
|
Primitive types will be assigned by value, any object values will be assigned by reference.
|
|
This is strongly inspired by the behaviour that most interpeters implement.
|
|
|
|
#### What happens if...
|
|
##### I assign an object by value?
|
|
The entire object will be deep-copied and transfered to the new location.
|
|
See it as a free clone method on basically anything that is considered an object.
|
|
##### I assign a primitive by reference?
|
|
So, assuming that `x` is a primitive (value type), given the instruction `*~ y <& x;`,
|
|
`y` will be assigned as a reference value _pointing_ to the location of `x` - somewhat like a
|
|
pointer in C, but a little smarter.
|
|
##### I do not praise the god emperor of mankind?
|
|
You will be duly punished.
|
|
|
|
#### Variable Declaration
|
|
|
|
```
|
|
*~ q <~ 0xf;
|
|
```
|
|
|
|
#### Variable Declaration
|
|
|
|
## Customization & Extras
|
|
|
|
Some more information about extending and customizing Q& to your needs.
|
|
|
|
### Implementing external Libraries
|
|
|
|
Implementation is easy.
|
|
Q& is written in C#, and supports basically anything you can wrap in C#.
|
|
All you have to do in order to implement a library is referencing the SDK dll
|
|
which should be located within your `./Build/` folder after building as described above.
|
|
|
|
Here's a quick example of how you would implement virtually any library:
|
|
```cs
|
|
using Qrakhen.Qamp.Core;
|
|
|
|
// This is an intentionally bad implementation of a linked list.
|
|
[ExportType("llist")]
|
|
public class LinkedListNode : Obj
|
|
{
|
|
[HideProperty]
|
|
public LinkedListNode? Next { get; private set; }
|
|
|
|
public Value Value { get; private set; }
|
|
|
|
public LinkedList(Value value) {
|
|
// ...internal setup
|
|
this.Value = value;
|
|
}
|
|
|
|
// Q& constructors, if needed, have to be explicitely declared to return a value type.
|
|
[ExportConstructor]
|
|
public static Value Ctor(params Value[] args) {
|
|
current = this;
|
|
while (current.Next != null)
|
|
current = current.Next;
|
|
|
|
// This simply returns a new pointer to an already existing
|
|
// oject inside the heap, rather than actually create a new one.
|
|
// It wraps a pointer to the provided object inside a value that
|
|
// has the 'Ptr' type flag, passing it to the Q& runtime.
|
|
return Obj.Create(current);
|
|
}
|
|
|
|
// Tell the type mapper to export this method
|
|
[ExportMethod]
|
|
public Value Last() {
|
|
// This simply returns a new pointer to an already existing type.
|
|
// The call ddoes not actually create a new object.
|
|
var current = this;
|
|
while (current.Next != null) {
|
|
current = current.Next;
|
|
return Obj.Create(current);
|
|
}
|
|
|
|
// Value is the base type of anything in Q&.
|
|
// An object is just a value of type 'Ptr' (value.IsPtr),
|
|
// and primitives have their value stored in the 8 byte
|
|
// buffer of the value itself (value.Data).
|
|
[ExportMethod]
|
|
public void Add(Value value) {
|
|
var current = this;
|
|
while (current.Next != null)
|
|
current = current.Next;
|
|
current.Next = new LinkedList(value);
|
|
}
|
|
|
|
// Methods that accept a primitive will automatically try
|
|
// retrieve that type from the provided arguments as Values.
|
|
// You may always simply declare to accept Values aswell,
|
|
// which gives you some dynamic flexibility if needed.
|
|
[ExportMethod]
|
|
public Value? GetItem(long index) {
|
|
long current = 0;
|
|
var node = this;
|
|
while (current < index) {
|
|
current++;
|
|
node = node.Next;
|
|
}
|
|
return node;
|
|
}
|
|
}
|
|
```
|
|
|
|
You may manually register all types inside an assembly by calling
|
|
```
|
|
Library:Load("mylib.dll");
|
|
```
|
|
directly in Q&.
|
|
Usage of the type described above would look somewhat like this:
|
|
```
|
|
*~ list = new llist();
|
|
list.Add(5);
|
|
list.Add('test');
|
|
list.Add([1, 2, 3]);
|
|
print(list.GetItem(1)); // 'test'
|
|
print(list.Last()); // [ 1, 2, 3 ]
|
|
*~ protected = list.Value; // will throw an exception, as we did not expose that property.
|
|
```
|
|
|
|
That's it - although this being a very simple example, more is possible.
|
|
I will add a few basic implementations of very common libraries in a separate repository soon.
|
|
|