From fb4b062fc9e51f833ab354617daf3f631f81ec14 Mon Sep 17 00:00:00 2001 From: Qrakhen Date: Sun, 9 Nov 2025 12:29:46 +0100 Subject: [PATCH] put strings on a hash table, serializiation for test purposes --- Qrakhen.Qamp.Core/Collections/Register.cs | 3 +- Qrakhen.Qamp.Core/Execution/Runner.cs | 3 +- Qrakhen.Qamp.Core/Execution/Segment.cs | 26 ++++--- Qrakhen.Qamp.Core/Extensions.cs | 17 ++++- Qrakhen.Qamp.Core/Tokenization/Reader.cs | 77 +++++++++++--------- Qrakhen.Qamp.Core/Values/Objects/Function.cs | 21 ++++++ Qrakhen.Qamp.Core/Values/Objects/Obj.cs | 5 +- Qrakhen.Qamp.Core/Values/Objects/String.cs | 49 +++++++++++-- Qrakhen.Qamp.Core/Values/Ptr.cs | 35 ++++++++- Qrakhen.Qamp.Core/Values/Value.cs | 17 ++++- README.md | 2 + 11 files changed, 195 insertions(+), 60 deletions(-) diff --git a/Qrakhen.Qamp.Core/Collections/Register.cs b/Qrakhen.Qamp.Core/Collections/Register.cs index c2f16bb..546d273 100644 --- a/Qrakhen.Qamp.Core/Collections/Register.cs +++ b/Qrakhen.Qamp.Core/Collections/Register.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Diagnostics.CodeAnalysis; namespace Qrakhen.Qamp.Core.Collections; @@ -26,7 +27,7 @@ public class Register : return key; } - public bool TryGet(TKey key, out TValue? value) => _data.TryGetValue(key, out value); + public bool TryGet(TKey key, [MaybeNullWhen(false)] out TValue value) => _data.TryGetValue(key, out value); public TValue Get(TKey key) => _data[key]; public void Set(TKey key, TValue value) => _data[key] = value; public bool Has(TKey key) => _data.ContainsKey(key); diff --git a/Qrakhen.Qamp.Core/Execution/Runner.cs b/Qrakhen.Qamp.Core/Execution/Runner.cs index 8ac9623..bad5bfb 100644 --- a/Qrakhen.Qamp.Core/Execution/Runner.cs +++ b/Qrakhen.Qamp.Core/Execution/Runner.cs @@ -60,7 +60,8 @@ public class Runner : IDisposable using Reader reader = new Reader(stream); Compilation.Digester digester = new(reader); Function function = digester.Digest(); - File.WriteAllBytes("func.sqi", function.Segment.Serialize()); + if (stream.Length > 64) + File.WriteAllBytes($"./func.{DateTime.Now:yyyyMMdd_HHmmss}.sqi", function.Serialize(true)); Context closure = new Context(function); Push(Obj.Create(closure)); Invoke(closure, 0); diff --git a/Qrakhen.Qamp.Core/Execution/Segment.cs b/Qrakhen.Qamp.Core/Execution/Segment.cs index bedfdf7..cd69c55 100644 --- a/Qrakhen.Qamp.Core/Execution/Segment.cs +++ b/Qrakhen.Qamp.Core/Execution/Segment.cs @@ -1,5 +1,6 @@ using Qrakhen.Qamp.Core.Collections; using Qrakhen.Qamp.Core.Values; +using Qrakhen.Qamp.Core.Values.Objects; namespace Qrakhen.Qamp.Core.Execution; @@ -59,20 +60,21 @@ public class Segment( public byte[] Serialize() { var opCodes = Instructions.Select(i => (byte)i).ToArray(); - var constants = Constants.Select(c => c.Unsigned.GetBytes()).ToArray(); - byte[] bytes = new byte[sizeof(long) * 2 + opCodes.Length + constants.Length * 8]; - Array.Copy(opCodes.LongLength.GetBytes(), 0, bytes, 0, sizeof(long)); - Array.Copy((constants.LongLength * sizeof(long)).GetBytes(), 0, bytes, sizeof(long), sizeof(long)); - int offset = sizeof(long) * 2; - for (long i = 0; i < opCodes.LongLength; i++) { - bytes[offset++] = opCodes[i]; - } - for (long i = 0; i < constants.LongLength; i++) { - for (int j = 0; j < sizeof(long); j++) { - bytes[offset++] = constants[i][j]; + var constants = Constants.ToArray(); + byte[] bytes = new byte[sizeof(long) + opCodes.Length]; + System.Array.Copy(opCodes.LongLength.GetBytes(), 0, bytes, 0, sizeof(long)); + System.Array.Copy(opCodes, 0, bytes, sizeof(long), opCodes.Length); + var list = new List(bytes); + foreach (var constant in constants) { + var data = constant.Signed.GetDynamicBytes(); + if (data.Length == 1) + list.Add(data[0]); + else { + list.Add((byte)(data.Length | 0x80)); + list.AddRange(data); } } - return bytes; + return list.ToArray(); } public static Segment Deserialize(byte[] data) diff --git a/Qrakhen.Qamp.Core/Extensions.cs b/Qrakhen.Qamp.Core/Extensions.cs index a98e257..c202ffe 100644 --- a/Qrakhen.Qamp.Core/Extensions.cs +++ b/Qrakhen.Qamp.Core/Extensions.cs @@ -1,10 +1,25 @@ -using System.Text.Json.Serialization; +using System.Text; using System.Text.RegularExpressions; namespace Qrakhen.Qamp.Core; public static class Extensions { + public static uint GetHash(this string str) + { + if (string.IsNullOrEmpty(str)) + return 0; + byte[] bytes = Encoding.ASCII.GetBytes(str); + unchecked { + uint hash = 216613661u; + for (int i = 0; i < bytes.Length; i++) { + hash ^= bytes[i]; + hash *= 16777619; + } + return hash; + } + } + public static bool TryMatch(this Regex regex, string str, out Match match) { match = regex.Match(str); diff --git a/Qrakhen.Qamp.Core/Tokenization/Reader.cs b/Qrakhen.Qamp.Core/Tokenization/Reader.cs index 8632c9c..13ed3fd 100644 --- a/Qrakhen.Qamp.Core/Tokenization/Reader.cs +++ b/Qrakhen.Qamp.Core/Tokenization/Reader.cs @@ -17,43 +17,52 @@ internal static partial class ReaderPatterns public static partial Regex IsHexadecimal(); public static readonly Dictionary Keywords = new(); + public static readonly Dictionary Aliases = new(); + + private static void Define(string key, TokenType type) + { + // not ideal, but momentarily better than setting up complex dialects + if (Keywords.ContainsValue(type)) + Aliases[key] = Keywords.First(v => v.Value == type).Key; + Keywords[key] = type; + } static ReaderPatterns() { - Keywords["false"] = False; - Keywords["true"] = True; - Keywords["null"] = Null; - Keywords["void"] = Null; - Keywords["and"] = And; - Keywords["else"] = Else; - Keywords["for"] = For; - Keywords["if"] = If; - Keywords["or"] = Or; - Keywords["this"] = This; - Keywords[".~"] = This; - Keywords["var"] = Var; - Keywords["*~"] = Var; - Keywords["while"] = While; - Keywords["do"] = Do; - Keywords["ref"] = Ref; - Keywords["function"] = Function; - Keywords["funqtion"] = Function; - Keywords["fq"] = Function; - Keywords["funq"] = Function; - Keywords["return"] = Return; - Keywords["<:"] = Return; - Keywords["class"] = Class; - Keywords["base"] = Base; - Keywords["^~"] = Base; - Keywords["typeof"] = TypeOf; - Keywords["?:"] = TypeOf; - Keywords["print"] = Print; - Keywords["::"] = Var; - Keywords["globals"] = PrintGlobals; - Keywords["stack"] = PrintStack; - Keywords["expr"] = PrintExpr; - Keywords["import"] = Import; - Keywords["export"] = Export; + Define("false", False); + Define("true", True); + Define("null", Null); + Define("void", Null); + Define("and", And); + Define("else", Else); + Define("for", For); + Define("if", If); + Define("or", Or); + Define("this", This); + Define(".~", This); + Define("var", Var); + Define("*~", Var); + Define("while", While); + Define("do", Do); + Define("ref", Ref); + Define("function", Function); + Define("funqtion", Function); + Define("fq", Function); + Define("funq", Function); + Define("return", Return); + Define("<:", Return); + Define("class", Class); + Define("base", Base); + Define("^~", Base); + Define("typeof", TypeOf); + Define("?:", TypeOf); + Define("print", Print); + Define("::", Print); + Define("globals", PrintGlobals); + Define("stack", PrintStack); + Define("expr", PrintExpr); + Define("import", Import); + Define("export", Export); } } diff --git a/Qrakhen.Qamp.Core/Values/Objects/Function.cs b/Qrakhen.Qamp.Core/Values/Objects/Function.cs index 2571709..6462f7d 100644 --- a/Qrakhen.Qamp.Core/Values/Objects/Function.cs +++ b/Qrakhen.Qamp.Core/Values/Objects/Function.cs @@ -1,4 +1,5 @@ using Qrakhen.Qamp.Core.Execution; +using System.Text; namespace Qrakhen.Qamp.Core.Values.Objects; @@ -10,4 +11,24 @@ public class Function(string name, Segment segment, int outerCount, int argument public readonly int ArgumentCount = argumentCount; public override string ToString() => $"{Name}()"; + + public byte[] Serialize(bool topLevel = false) + { + List result = + [ + .. string.IsNullOrEmpty(Name) ? [] : Encoding.ASCII.GetBytes(Name), + 0, + (byte)ArgumentCount, + .. OuterCount.GetBytes(), + .. Segment.Serialize() + ]; + if (topLevel) { + result.AddRange([0xFF, 0xF0]); + result.AddRange(String.SerializeStrings()); + result.AddRange([0x0F, 0xFF, 0xFF, 0xF1]); + result.AddRange(Ptr.SerializeFunctions()); + result.AddRange([0x0F, 0xFF]); + } + return result.ToArray(); + } } \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Objects/Obj.cs b/Qrakhen.Qamp.Core/Values/Objects/Obj.cs index 9f92401..baf7c17 100644 --- a/Qrakhen.Qamp.Core/Values/Objects/Obj.cs +++ b/Qrakhen.Qamp.Core/Values/Objects/Obj.cs @@ -2,13 +2,16 @@ public class Obj(ValueType type) : IValue { + public bool __gcMarked { get; private set; } + public int __gcCount { get; private set; } = 1; + public readonly ValueType Type = type; public ValueType ValueType => Type; public static Value Create(Obj obj) { - return new Value(new Ptr(obj)); + return new Value(Ptr.Create(obj)); } public override string ToString() => "Obj"; diff --git a/Qrakhen.Qamp.Core/Values/Objects/String.cs b/Qrakhen.Qamp.Core/Values/Objects/String.cs index f7b04a6..446f54a 100644 --- a/Qrakhen.Qamp.Core/Values/Objects/String.cs +++ b/Qrakhen.Qamp.Core/Values/Objects/String.cs @@ -1,13 +1,50 @@ -namespace Qrakhen.Qamp.Core.Values.Objects; +using Qrakhen.Qamp.Core.Collections; +using System.Text; + +namespace Qrakhen.Qamp.Core.Values.Objects; public class String(string? value) : Obj(ValueType.String) { + private static readonly Register _strings = new(); + public string? Value = value; - - public static Value Make(string? value) - { - return Create(new String(value)); - } public override string ToString() => Value ?? "null"; + + public uint GetHash() + { + if (string.IsNullOrEmpty(value)) + return 0; + + return Value.GetHash(); + } + + public static Value Make(string? value) + { + String? str; + var hash = value?.GetHash() ?? 0; + if (!_strings.TryGet(hash, out str)) { + str = new String(value); + _strings.Add(hash, str); + } + + return Create(str); + } + + public static byte[] SerializeStrings() + { + List result = new(); + foreach (var pair in _strings) { + String str = pair.Value; + if (str == null || str.__gcMarked) + continue; + byte[] bytes; + bytes = Encoding.ASCII.GetBytes(str.Value!); + result.AddRange(pair.Key.GetBytes()); + result.AddRange(bytes.Length.GetBytes()); + result.AddRange(bytes); + result.Add(0); + } + return result.ToArray(); + } } \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Ptr.cs b/Qrakhen.Qamp.Core/Values/Ptr.cs index 01cdca9..649eac7 100644 --- a/Qrakhen.Qamp.Core/Values/Ptr.cs +++ b/Qrakhen.Qamp.Core/Values/Ptr.cs @@ -2,13 +2,15 @@ using System.Runtime.InteropServices; using Qrakhen.Qamp.Core.Collections; using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; +using System.Text; namespace Qrakhen.Qamp.Core.Values; [StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))] public readonly struct Ptr { - private static readonly PushStack _register = new(0x10); + private static readonly PushStack _register = new(); [Serialized] public readonly Address Address; @@ -26,10 +28,15 @@ public readonly struct Ptr } } - public Ptr(Obj obj) + private Ptr(Address address) { //_pointer = GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Normal)); - Address = _register.Add(obj); + Address = address; + } + + public static Ptr Create(Obj obj) + { + return new Ptr(_register.Add(obj)); } public T? As(bool throwWhenNull = true) where T : Obj @@ -41,4 +48,26 @@ public readonly struct Ptr } public override string ToString() => $"0x{Value}"; + + public static byte[] SerializeFunctions() + { + List result = new(); + for (int i = 0; i < _register.Count; i++) { + Obj obj = _register.Get(i); + if (obj == null || obj.__gcMarked) + continue; + byte[] bytes; + if (obj is Function function) { + bytes = function.Serialize(); + } else { + continue; + } + + result.AddRange(i.GetBytes()); + result.AddRange(bytes.Length.GetBytes()); + result.AddRange(bytes); + result.Add(0); + } + return result.ToArray(); + } } \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Value.cs b/Qrakhen.Qamp.Core/Values/Value.cs index 379a9b4..eb64c00 100644 --- a/Qrakhen.Qamp.Core/Values/Value.cs +++ b/Qrakhen.Qamp.Core/Values/Value.cs @@ -12,7 +12,7 @@ public interface IValue } [StructLayout(LayoutKind.Explicit, Size = 0x10)] -public readonly struct Value : IValue, IDebug +public readonly struct Value : IValue, ISerialize, IDebug { public static readonly Value Void = new Value(); public static readonly Value True = new Value(true); @@ -140,6 +140,21 @@ public readonly struct Value : IValue, IDebug return str + string.Join("\n - ", lines); } + + public byte[] Serialize() + { + List result = [.. ((ushort)Type).GetBytes() ]; + byte[] bytes = Signed.GetBytes(); + result.Add((byte)bytes.Length); + result.AddRange(bytes); + result.Add(0x7F); + return result.ToArray(); + } + + public static Value Deserialize(byte[] data) + { + return Void; + } } public class SerializedAttribute : Attribute; \ No newline at end of file diff --git a/README.md b/README.md index 5a62815..b87c8b3 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ Most basic Usage: # This is a comment. *~ q <~ 12; *~ f <~ (n) <: n < 2 ? n : f(n-1) + f(n-2); +*~ a <~ [1, 2, 3]; +*~ x <~ a:0 + a:2; # = 4 ``` ### Assignments