qamp/README.md

6.2 KiB

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:

                                            # 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:

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.