qamp/Qrakhen.Qamp.Core/README.md

152 lines
4.8 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
#### Types
```
sig a; signed
uns b; unsigned
dec c; decimal
str d; string
chr e; character
bin f; boolean or binary, you can declare bitfields with:
bin:16 bits; custom bitfield type with 16 bits.
ref g; reference type, can be explicitely declared:
ref:sig aRef <& a;
ptr p; pointer, essentially the same as a ref, but in Q& ptr is the base type of all objects.
CustomType yourObject; self-explanatory
any type may be declared as [] array,
{} dictionary, or [:] list. (non-fixed collection)
```
#### Operators, Keywords & their aliases
```
*~ var
:~ const
<: return
:> continue
<+ increment / add
<- subtract / remove
:: print
?: typeof
<& assign reference
^~ base
.~ this
~( function
```
## Customization & Extras
Some more information about extending and customizing Q& to your needs.
### Writing Extension Methods
asdasd
### 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;
using System.Math;
Value Sqrt(Value number)
{
Assert.IsNumber(number);
if (number.IsDecimal)
return Math.Sqrt(number.AsDecimal);
if (number.IsSigned)
return Math.Sqrt(number.AsSigned);
if (number.IsUnsigned)
return Math.Sqrt(number.Unsigned);
throw new Qrakhen.Qamp.Core.QampException($"Unsupported value type {value}");
}
Injector injector = new();
injector.Register(scope: Scope.Global, name: "sqrt", params: [ ("number", ValueType.Number) ], returns: ValueType.Number);
injector.Export("desiredPath.sqi");
```
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.