better build, global native functions (spicy C style), some nice things, some bad things

This commit is contained in:
Qrakhen 2026-05-08 03:13:53 +02:00
parent 1da47d39dd
commit 1bbaa98add
11 changed files with 182 additions and 27 deletions

View File

@ -1,14 +0,0 @@
print("\nQ& Test script.");
*~ how <~ 'very cool';
var _WHAT_ = "ABQ&%$#@!";
var a = 7;
*~ b <~ 12;
var c <~ 8;
*~ d = 3;
:: _WHAT_.SubString(2, 2) + ' is ' + how;
print(a * b * c * d);
:: "if you can read this, Q& probably works. Wow."

View File

@ -102,6 +102,69 @@ public class Runner : IDisposable
IO.Console.StdOut ??= options.StdOut; IO.Console.StdOut ??= options.StdOut;
IO.Console.StdIn ??= options.StdIn; IO.Console.StdIn ??= options.StdIn;
Globals["time"] = Obj.Create(new NativeFunction("time",
v => new Value(DateTime.Now.Ticks / 100)));
Globals["ftime"] = Obj.Create(new NativeFunction("ftime",
v =>
String.Make(
new DateTime(v[0].Signed * 10)
.ToString(
v[1].GetString() ??
Timestamp.DefaultFormat)),
"time",
"format"));
Globals["sin"] = Obj.Create(new NativeFunction("sin",
v => new Value(Math.Sin(v[0].GetDecimal())), "v"));
Globals["cos"] = Obj.Create(new NativeFunction("cos",
v => new Value(Math.Cos(v[0].GetDecimal())), "v"));
Globals["exit"] = Obj.Create(new NativeFunction("exit", v => {
Environment.Exit((int)(v.FirstOrDefault().Signed));
return Value.Void;
},
"code?"));
Globals["read"] = Obj.Create(new NativeFunction("read", v =>
String.Make(IO.Console.StdIn())));
Globals["write"] = Obj.Create(new NativeFunction("write", v => {
try {
if (v[0].GetString() is string content) {
IO.Console.StdOut(content);
}
} catch { }
return Value.Void;
},
"content"));
Globals["fread"] = Obj.Create(new NativeFunction("fread", v =>
(v[0].GetString() is string path && File.Exists(Path.GetFullPath(path))) ?
String.Make(
File.ReadAllText(
Path.GetFullPath(path))) :
Value.Void,
"path"));
Globals["fwrite"] = Obj.Create(new NativeFunction("fwrite", v => {
try {
if (v[0].GetString() is string path) {
File.WriteAllText(
path,
v[1].GetString());
} else {
return Value.False;
}
} catch {
return Value.False;
}
return Value.True;
},
"path",
"content?"));
_router.Register(new ArithmeticResolver()); _router.Register(new ArithmeticResolver());
} }
@ -569,6 +632,27 @@ public class Runner : IDisposable
return true; return true;
} }
private bool InvokeNative(NativeFunction native, int argumentCount)
{
#if LOG
_logger.Method(native);
#endif
if (argumentCount != native.Arguments.Length) {
Error($"{native} expects {native.Arguments.Length} arguments, received {argumentCount}");
return false;
}
var values = new Value[argumentCount];
for (int i = 0; i < argumentCount; i++) {
values[i] = Peek(-i - 1);
}
_stack.Decimate(_stack.Position - argumentCount - 1);
Value result = native.Target.Invoke(values);
Push(result);
return true;
}
private bool Invoke(Context closure, int argumentCount) private bool Invoke(Context closure, int argumentCount)
{ {
#if LOG #if LOG
@ -620,19 +704,19 @@ public class Runner : IDisposable
private bool Invoke(Value value, int argumentCount) private bool Invoke(Value value, int argumentCount)
{ {
if (!value.IsObj) { if (!value.IsObj) {
Error($"Can only call values of object type! Tried to call {value.ToString(true)}", value); Error($"Can only invoke non-primitive values! Tried to invoke {value.ToString(true)}", value);
return false; return false;
} }
if (value.Is(T.Method)) { if (value.Is(T.Native)) {
return InvokeNative(value.Ptr.As<NativeFunction>(), argumentCount);
} else if (value.Is(T.Method)) {
Method method = value.Ptr.As<Method>()!; Method method = value.Ptr.As<Method>()!;
_stack.Set(_cursor - method.Function.ArgumentCount - 1, method.Receiver); _stack.Set(_cursor - method.Function.ArgumentCount - 1, method.Receiver);
return Invoke(method, method.Function.ArgumentCount); return Invoke(method, method.Function.ArgumentCount);
} else if (value.Is(T.Context)) { } else if (value.Is(T.Context)) {
Context? context = value.Ptr.As<Context>()!; Context? context = value.Ptr.As<Context>()!;
return Invoke(context, context.Function.ArgumentCount); return Invoke(context, context.Function.ArgumentCount);
} else if (value.Is(T.Native)) {
throw new NotImplementedException("i dont really want globals in this language so lets see");
} else if (value.Is(T.Class)) { } else if (value.Is(T.Class)) {
Class? @class = value.Ptr.As<Class>()!; Class? @class = value.Ptr.As<Class>()!;
_stack.Set(_cursor - argumentCount - 1, Instantiate(@class)); _stack.Set(_cursor - argumentCount - 1, Instantiate(@class));

View File

@ -0,0 +1,23 @@
using System;
namespace Qrakhen.Qamp.Core.Values.Objects;
public class Timestamp(Value timeStamp) : Native(nameof(Timestamp))
{
public static readonly string DefaultFormat = "H:m:s d.M.y";
// cheap ass workaround i know
private DateTime _dateTime = new DateTime(timeStamp.Signed * 10);
public Value Raw = timeStamp;
public Value Format(Value? format = null)
{
return String.Make(_dateTime.ToString(format?.Ptr.As<String>()?.Value ?? DefaultFormat));
}
public override string ToString() => ToString(true);
public string ToString(bool detail) => detail ?
$"[Timestamp <{_dateTime}>]" :
_dateTime.ToString(DefaultFormat);
}

View File

@ -31,4 +31,16 @@ public class Function(string name, Segment segment, int outerCount, int argument
} }
return result.ToArray(); return result.ToArray();
} }
} }
// local c# function, so compiled inside the binary already
// cheap shortcut for global methods for now.
public class NativeFunction(string name, Func<Value[], Value> target, params string[] arguments) : Obj(ValueType.Native)
{
public string Name { get; } = name;
public Func<Value[], Value> Target { get; } = target;
public string[] Arguments { get; } = arguments;
public override string ToString() => $"[NativeFunction <{Name}> ({string.Join(", ", Arguments)})]";
}

View File

@ -4,3 +4,4 @@ public class Method(Function function, Value receiver) : Context(function, Value
{ {
public Value Receiver = receiver; public Value Receiver = receiver;
} }

View File

@ -5,6 +5,7 @@ namespace Qrakhen.Qamp.Core.Values.Objects;
public class String(string? value) : Obj(ValueType.String) public class String(string? value) : Obj(ValueType.String)
{ {
// yea that's quite temporary
private static readonly Register<uint, String> _strings = new(); private static readonly Register<uint, String> _strings = new();
public string? Value = value; public string? Value = value;

View File

@ -102,6 +102,11 @@ public readonly struct Value :
public bool IsFalsy => IsBool ? Bool == false : Unsigned == 0; public bool IsFalsy => IsBool ? Bool == false : Unsigned == 0;
// another shortcut that will cost me greatly
public String? AsString() => Ptr.As<String>();
public string? GetString() => AsString()?.Value;
public double GetDecimal() => IsDecimal ? Decimal : (double)Signed;
public bool Is(T type, bool exact = true) public bool Is(T type, bool exact = true)
{ {
return exact ? (Type & type) == type : (Type & type) > 0; return exact ? (Type & type) == type : (Type & type) > 0;

View File

@ -5,9 +5,9 @@
/// edit: if that's even possible with c#'s memory padding /// edit: if that's even possible with c#'s memory padding
/// </summary> /// </summary>
[Flags] [Flags]
public enum ValueType public enum ValueType : ushort
{ {
Undefined = -1, Undefined = 0xFFFF,
Void = 0x0000, Void = 0x0000,
Unsigned = 0x0001, Unsigned = 0x0001,
Signed = 0x0002, Signed = 0x0002,
@ -26,7 +26,7 @@ public enum ValueType
Reference = 0x1000, Reference = 0x1000,
Pointer = 0x2000, Pointer = 0x2000,
Object = Pointer | 0x4000, Object = Pointer | 0x2000,
Native = Object | 0x0001, Native = Object | 0x0001,
String = Object | 0x0002, String = Object | 0x0002,
Function = Object | 0x0020, Function = Object | 0x0020,
@ -36,10 +36,12 @@ public enum ValueType
Method = Class | Function, Method = Class | Function,
Outer = Object | 0x0200, Outer = Object | 0x0200,
ItemProvider = Object | 0x8000, // accessible with [n] or :n (getters / setters) Complex = Object | 0x0800,
ItemProvider = Object | 0x4000, // accessible with [n] or :n (getters / setters)
Array = ItemProvider | 0x0001, Array = ItemProvider | 0x0001,
List = ItemProvider | 0x0002, List = ItemProvider | 0x0002,
Structure = ItemProvider | 0x0004, Structure = ItemProvider | 0x0004,
Module = 0xFFFF Module = 0x8000
} }

32
Static/test.qp Normal file
View File

@ -0,0 +1,32 @@
print("\nQ& Test script.");
*~ how <~ 'very cool';
var _WHAT_ = "ABQ&%$#@!";
var a = 7;
*~ b <~ 12;
var c <~ 8;
*~ d = 3;
:: _WHAT_.SubString(2, 2) + ' is ' + how;
print(a * b * c * d);
if (a > d) {
:: "compare operator is sane";
} else {
:: "sanity check failed";
}
for (*~i<~1;i<=16;i++) {
:: "[" + i + "]: " + i*i;
}
fq fib(n) { if (n<2) <: 1; <: fib(n-1) + fib(n-2); }
*~ fibs <~ 'fibonacci sequence:';
for (*~i<~0;i<16;i++) {
fibs = fibs + " " + fib(i);
}
:: fibs;
:: "if you can read this, Q& probably works. Wow."

11
build Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
DEST='net10.0'
FOUT='./Build/'
DATE=`date`
mkdir -p $FOUT
cp -r ./Static/ $FOUT
echo "$DEST" > "$FOUT.qpb"
echo "$date" >> "$FOUT.qpb"
dotnet build ./Qrakhen.Qamp.CLI/ -o $FOUT

4
debug
View File

@ -1,5 +1,3 @@
#!/bin/bash #!/bin/bash
DEST='net10.0' source ./build && ./Build/Qrakhen.Qamp.CLI -i "$FOUT/Static/test.qp"
echo "\e[5mBeelding for dotnet version $DEST like it's not a big deal.\e[0m"
dotnet build ./Qrakhen.Qamp.CLI/ && ./Build/Debug/net10.0/Qrakhen.Qamp.CLI -i ./Qrakhen.Qamp.CLI/test.qp