From 1bbaa98add08784fe9596b371c7b6a9bedfa20d6 Mon Sep 17 00:00:00 2001 From: Qrakhen Date: Fri, 8 May 2026 03:13:53 +0200 Subject: [PATCH] better build, global native functions (spicy C style), some nice things, some bad things --- Qrakhen.Qamp.CLI/test.qp | 14 --- Qrakhen.Qamp.Core/Execution/Runner.cs | 92 ++++++++++++++++++- .../Values/Objects/Complex/Timestamp.cs | 23 +++++ Qrakhen.Qamp.Core/Values/Objects/Function.cs | 14 ++- Qrakhen.Qamp.Core/Values/Objects/Method.cs | 1 + Qrakhen.Qamp.Core/Values/Objects/String.cs | 1 + Qrakhen.Qamp.Core/Values/Value.cs | 5 + Qrakhen.Qamp.Core/Values/ValueType.cs | 12 ++- Static/test.qp | 32 +++++++ build | 11 +++ debug | 4 +- 11 files changed, 182 insertions(+), 27 deletions(-) delete mode 100644 Qrakhen.Qamp.CLI/test.qp create mode 100644 Qrakhen.Qamp.Core/Values/Objects/Complex/Timestamp.cs create mode 100644 Static/test.qp create mode 100755 build diff --git a/Qrakhen.Qamp.CLI/test.qp b/Qrakhen.Qamp.CLI/test.qp deleted file mode 100644 index 48468ca..0000000 --- a/Qrakhen.Qamp.CLI/test.qp +++ /dev/null @@ -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." \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Execution/Runner.cs b/Qrakhen.Qamp.Core/Execution/Runner.cs index f0e66a8..49de174 100644 --- a/Qrakhen.Qamp.Core/Execution/Runner.cs +++ b/Qrakhen.Qamp.Core/Execution/Runner.cs @@ -102,6 +102,69 @@ public class Runner : IDisposable IO.Console.StdOut ??= options.StdOut; 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()); } @@ -569,6 +632,27 @@ public class Runner : IDisposable 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) { #if LOG @@ -620,19 +704,19 @@ public class Runner : IDisposable private bool Invoke(Value value, int argumentCount) { 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; } - if (value.Is(T.Method)) { + if (value.Is(T.Native)) { + return InvokeNative(value.Ptr.As(), argumentCount); + } else if (value.Is(T.Method)) { Method method = value.Ptr.As()!; _stack.Set(_cursor - method.Function.ArgumentCount - 1, method.Receiver); return Invoke(method, method.Function.ArgumentCount); } else if (value.Is(T.Context)) { Context? context = value.Ptr.As()!; 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)) { Class? @class = value.Ptr.As()!; _stack.Set(_cursor - argumentCount - 1, Instantiate(@class)); diff --git a/Qrakhen.Qamp.Core/Values/Objects/Complex/Timestamp.cs b/Qrakhen.Qamp.Core/Values/Objects/Complex/Timestamp.cs new file mode 100644 index 0000000..b72e529 --- /dev/null +++ b/Qrakhen.Qamp.Core/Values/Objects/Complex/Timestamp.cs @@ -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()?.Value ?? DefaultFormat)); + } + + public override string ToString() => ToString(true); + public string ToString(bool detail) => detail ? + $"[Timestamp <{_dateTime}>]" : + _dateTime.ToString(DefaultFormat); +} \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Objects/Function.cs b/Qrakhen.Qamp.Core/Values/Objects/Function.cs index c607ad1..b55aa83 100644 --- a/Qrakhen.Qamp.Core/Values/Objects/Function.cs +++ b/Qrakhen.Qamp.Core/Values/Objects/Function.cs @@ -31,4 +31,16 @@ public class Function(string name, Segment segment, int outerCount, int argument } return result.ToArray(); } -} \ No newline at end of file +} + +// local c# function, so compiled inside the binary already +// cheap shortcut for global methods for now. +public class NativeFunction(string name, Func target, params string[] arguments) : Obj(ValueType.Native) +{ + public string Name { get; } = name; + public Func Target { get; } = target; + + public string[] Arguments { get; } = arguments; + + public override string ToString() => $"[NativeFunction <{Name}> ({string.Join(", ", Arguments)})]"; +} diff --git a/Qrakhen.Qamp.Core/Values/Objects/Method.cs b/Qrakhen.Qamp.Core/Values/Objects/Method.cs index 66058eb..3ed33f8 100644 --- a/Qrakhen.Qamp.Core/Values/Objects/Method.cs +++ b/Qrakhen.Qamp.Core/Values/Objects/Method.cs @@ -4,3 +4,4 @@ public class Method(Function function, Value receiver) : Context(function, Value { public Value Receiver = receiver; } + diff --git a/Qrakhen.Qamp.Core/Values/Objects/String.cs b/Qrakhen.Qamp.Core/Values/Objects/String.cs index cf89807..fc7dca1 100644 --- a/Qrakhen.Qamp.Core/Values/Objects/String.cs +++ b/Qrakhen.Qamp.Core/Values/Objects/String.cs @@ -5,6 +5,7 @@ namespace Qrakhen.Qamp.Core.Values.Objects; public class String(string? value) : Obj(ValueType.String) { + // yea that's quite temporary private static readonly Register _strings = new(); public string? Value = value; diff --git a/Qrakhen.Qamp.Core/Values/Value.cs b/Qrakhen.Qamp.Core/Values/Value.cs index e01cf10..4ed69d5 100644 --- a/Qrakhen.Qamp.Core/Values/Value.cs +++ b/Qrakhen.Qamp.Core/Values/Value.cs @@ -102,6 +102,11 @@ public readonly struct Value : public bool IsFalsy => IsBool ? Bool == false : Unsigned == 0; + // another shortcut that will cost me greatly + public String? AsString() => Ptr.As(); + public string? GetString() => AsString()?.Value; + public double GetDecimal() => IsDecimal ? Decimal : (double)Signed; + public bool Is(T type, bool exact = true) { return exact ? (Type & type) == type : (Type & type) > 0; diff --git a/Qrakhen.Qamp.Core/Values/ValueType.cs b/Qrakhen.Qamp.Core/Values/ValueType.cs index fe7a8c9..8ff2ca4 100644 --- a/Qrakhen.Qamp.Core/Values/ValueType.cs +++ b/Qrakhen.Qamp.Core/Values/ValueType.cs @@ -5,9 +5,9 @@ /// edit: if that's even possible with c#'s memory padding /// [Flags] -public enum ValueType +public enum ValueType : ushort { - Undefined = -1, + Undefined = 0xFFFF, Void = 0x0000, Unsigned = 0x0001, Signed = 0x0002, @@ -26,7 +26,7 @@ public enum ValueType Reference = 0x1000, Pointer = 0x2000, - Object = Pointer | 0x4000, + Object = Pointer | 0x2000, Native = Object | 0x0001, String = Object | 0x0002, Function = Object | 0x0020, @@ -36,10 +36,12 @@ public enum ValueType Method = Class | Function, 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, List = ItemProvider | 0x0002, Structure = ItemProvider | 0x0004, - Module = 0xFFFF + Module = 0x8000 } diff --git a/Static/test.qp b/Static/test.qp new file mode 100644 index 0000000..f0fe825 --- /dev/null +++ b/Static/test.qp @@ -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." \ No newline at end of file diff --git a/build b/build new file mode 100755 index 0000000..2fdabde --- /dev/null +++ b/build @@ -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 diff --git a/debug b/debug index 053d0ea..9158b6b 100755 --- a/debug +++ b/debug @@ -1,5 +1,3 @@ #!/bin/bash -DEST='net10.0' -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 +source ./build && ./Build/Qrakhen.Qamp.CLI -i "$FOUT/Static/test.qp"