Sicherung
This commit is contained in:
28
Kalk/Kalk.Core/Modules/AllModule.cs
Normal file
28
Kalk/Kalk.Core/Modules/AllModule.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// Import all modules.
|
||||
/// </summary>
|
||||
[KalkExportModule("All")]
|
||||
public partial class AllModule : KalkModule
|
||||
{
|
||||
public AllModule()
|
||||
{
|
||||
Modules = new List<KalkModule>();
|
||||
RegisterDocumentationAuto();
|
||||
}
|
||||
|
||||
public List<KalkModule> Modules { get; }
|
||||
|
||||
protected override void Import()
|
||||
{
|
||||
base.Import();
|
||||
foreach (var module in Modules)
|
||||
{
|
||||
module.InternalImport();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
83
Kalk/Kalk.Core/Modules/CsvModule.cs
Normal file
83
Kalk/Kalk.Core/Modules/CsvModule.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Kalk.Core.Helpers;
|
||||
using Scriban.Runtime;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// Module for loading/parsing CSV text/files.
|
||||
/// </summary>
|
||||
[KalkExportModule(ModuleName)]
|
||||
public partial class CsvModule : KalkModuleWithFunctions
|
||||
{
|
||||
private const string ModuleName = "Csv";
|
||||
|
||||
public CsvModule() : base(ModuleName)
|
||||
{
|
||||
RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the specified text as a CSV, returning each CSV line in an array.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to parse.</param>
|
||||
/// <param name="headers"><c>true</c> if the text to parse has CSV headers. Default is fault.</param>
|
||||
/// <returns>An array of CSV columns values.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> items = parse_csv("a,b,c\n1,2,3\n4,5,6\n")
|
||||
/// # items = parse_csv("a,b,c\n1,2,3\n4,5,6\n")
|
||||
/// items = [[1, 2, 3], [4, 5, 6]]
|
||||
/// >>> items[0].a
|
||||
/// # items[0].a
|
||||
/// out = 1
|
||||
/// >>> items[0].b
|
||||
/// # items[0].b
|
||||
/// out = 2
|
||||
/// >>> items[0].c
|
||||
/// # items[0].c
|
||||
/// out = 3
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("parse_csv", StringModule.CategoryString)]
|
||||
public ScriptRange ParseCsv(string text, bool headers = true)
|
||||
{
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
return new ScriptRange(new KalkCsvReader(() => new StringReader(text), headers));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the specified file as a CSV, returning each CSV line in an array.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path to load and parse as CSV.</param>
|
||||
/// <param name="headers"><c>true</c> if the file to parse has CSV headers. Default is fault.</param>
|
||||
/// <returns>An array of CSV columns values.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> items = load_csv("test.csv")
|
||||
/// # items = load_csv("test.csv")
|
||||
/// items = [[1, 2, 3], [4, 5, 6]]
|
||||
/// >>> items[0].a
|
||||
/// # items[0].a
|
||||
/// out = 1
|
||||
/// >>> items[1].a
|
||||
/// # items[1].a
|
||||
/// out = 4
|
||||
/// >>> items[1].c
|
||||
/// # items[1].c
|
||||
/// out = 6
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("load_csv", FileModule.CategoryMiscFile)]
|
||||
public ScriptRange LoadCsv(string path, bool headers = true)
|
||||
{
|
||||
var fullPath = Engine.FileModule.AssertReadFile(path);
|
||||
return new ScriptRange(new KalkCsvReader(() =>
|
||||
{
|
||||
var stream = Engine.FileService.FileOpen(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return new StreamReader(stream);
|
||||
}, headers));
|
||||
}
|
||||
}
|
||||
}
|
||||
428
Kalk/Kalk.Core/Modules/Currencies/CurrencyModule.cs
Normal file
428
Kalk/Kalk.Core/Modules/Currencies/CurrencyModule.cs
Normal file
File diff suppressed because one or more lines are too long
139
Kalk/Kalk.Core/Modules/Currencies/KalkCurrencies.cs
Normal file
139
Kalk/Kalk.Core/Modules/Currencies/KalkCurrencies.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kalk.Core.Modules;
|
||||
using Scriban;
|
||||
using Scriban.Parsing;
|
||||
using Scriban.Runtime;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public class KalkCurrencies : IScriptObject, IScriptCustomFunction
|
||||
{
|
||||
private KalkUnits _units;
|
||||
private readonly CurrencyModule _currencyModule;
|
||||
|
||||
public KalkCurrencies(CurrencyModule currencyModule)
|
||||
{
|
||||
_currencyModule = currencyModule;
|
||||
}
|
||||
|
||||
public void Initialize(KalkEngine engine)
|
||||
{
|
||||
if (engine == null) throw new ArgumentNullException(nameof(engine));
|
||||
_units = engine.Units;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var unitPair in _units)
|
||||
{
|
||||
if (unitPair.Value is KalkCurrency) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
var keys = _units.Keys.ToList();
|
||||
foreach (var unitKey in keys)
|
||||
{
|
||||
if (_units.TryGetValue(unitKey, out var unitObject) && unitObject is KalkCurrency)
|
||||
{
|
||||
_units.Remove(unitKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetMembers()
|
||||
{
|
||||
// Show only currencies from units
|
||||
foreach (var unitPair in _units)
|
||||
{
|
||||
if (unitPair.Value is KalkCurrency) yield return unitPair.Key;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(string member)
|
||||
{
|
||||
return _units.Contains(member) && _units[member] is KalkCurrency;
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get => false;
|
||||
set => _units.IsReadOnly = value;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TemplateContext context, SourceSpan span, string member, out object value)
|
||||
{
|
||||
if (_units.TryGetValue(context, span, member, out value) && value is KalkCurrency)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanWrite(string member) => false;
|
||||
|
||||
public bool TrySetValue(TemplateContext context, SourceSpan span, string member, object value, bool readOnly) => false;
|
||||
|
||||
public bool Remove(string member) => false;
|
||||
|
||||
public void SetReadOnly(string member, bool readOnly)
|
||||
{
|
||||
}
|
||||
|
||||
public IScriptObject Clone(bool deep)
|
||||
{
|
||||
throw new NotSupportedException("Clone is not supported");
|
||||
}
|
||||
|
||||
public int RequiredParameterCount => 0;
|
||||
|
||||
public int ParameterCount => 0;
|
||||
|
||||
public ScriptVarParamKind VarParamKind => ScriptVarParamKind.None;
|
||||
|
||||
public Type ReturnType => typeof(object);
|
||||
|
||||
public ScriptParameterInfo GetParameterInfo(int index)
|
||||
{
|
||||
throw new NotSupportedException("Currencies don't have any parameters.");
|
||||
}
|
||||
|
||||
public object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
|
||||
{
|
||||
if (!(callerContext.Parent is ScriptExpressionStatement))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var engine = (KalkEngine) context;
|
||||
if (_units.All(x => x.Value.GetType() != typeof(KalkCurrency)))
|
||||
{
|
||||
engine.WriteHighlightLine($"# No Currencies defined (e.g try `import {nameof(CurrencyModule)}`)");
|
||||
}
|
||||
else
|
||||
{
|
||||
_units.Display(engine, $"Builtin Currencies (Last Update: {_currencyModule.LastUpdate.ToString("dd MMM yyyy", CultureInfo.InvariantCulture)})", symbol => symbol is KalkCurrency && !symbol.IsUser, false);
|
||||
_units.Display(engine, "User Defined Currencies", symbol => symbol is KalkCurrency && symbol.IsUser, false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ValueTask<object> InvokeAsync(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
|
||||
{
|
||||
return new ValueTask<object>(Invoke(context, callerContext, arguments, blockStatement));
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Kalk/Kalk.Core/Modules/Currencies/KalkCurrency.cs
Normal file
80
Kalk/Kalk.Core/Modules/Currencies/KalkCurrency.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Kalk.Core.Modules;
|
||||
using Scriban;
|
||||
using Scriban.Parsing;
|
||||
using Scriban.Runtime;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public class KalkCurrency : KalkUnit
|
||||
{
|
||||
private readonly CurrencyModule _currencyModule;
|
||||
private const int CurrencyColumnAlign = 27;
|
||||
|
||||
public KalkCurrency(CurrencyModule currencyModule, string name) : base(name)
|
||||
{
|
||||
_currencyModule = currencyModule;
|
||||
Plural = name;
|
||||
}
|
||||
|
||||
public override string TypeName => "currency";
|
||||
|
||||
public override object GetValue() => 1.0m;
|
||||
|
||||
public override object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
|
||||
{
|
||||
if (!(callerContext.Parent is ScriptExpressionStatement))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
var engine = (KalkEngine)context;
|
||||
|
||||
string currencyCmd;
|
||||
string currencyDesc;
|
||||
if (Value == null)
|
||||
{
|
||||
currencyCmd = $"currency({Name});";
|
||||
currencyDesc = $"Base currency";
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (KalkBinaryExpression) Value;
|
||||
|
||||
var valueToBase = 1.0m / (decimal) value.Value;
|
||||
var format = "#0.0###";
|
||||
if (valueToBase < 0.0001m)
|
||||
{
|
||||
format = null;
|
||||
}
|
||||
|
||||
var formattedNumber = valueToBase.ToString(format, CultureInfo.InvariantCulture);
|
||||
|
||||
currencyCmd = $"currency({Name}, {formattedNumber});";
|
||||
currencyDesc = $"{formattedNumber,-8} {Name} => 1 {_currencyModule.GetSafeBaseCurrencyFromConfig().Name}";
|
||||
}
|
||||
|
||||
engine.WriteHighlightLine($"{currencyCmd, -CurrencyColumnAlign} # {currencyDesc}");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void CheckValid(SourceSpan span, string name)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (name.Length != 3) throw new ScriptRuntimeException(span, $"Base currency `{name}` must 3 characters long instead of {name.Length}.");
|
||||
foreach (var c in name)
|
||||
{
|
||||
if (!(c >= 'A' && c <= 'Z'))
|
||||
{
|
||||
throw new ScriptRuntimeException(span, $"The character `{c}` is invalid for the base currency `{name}`. Only A-Z are allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
497
Kalk/Kalk.Core/Modules/FileModule.cs
Normal file
497
Kalk/Kalk.Core/Modules/FileModule.cs
Normal file
@@ -0,0 +1,497 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Kalk.Core.Helpers;
|
||||
using Scriban.Runtime;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// Modules providing file related functions.
|
||||
/// </summary>
|
||||
[KalkExportModule(ModuleName)]
|
||||
public partial class FileModule : KalkModuleWithFunctions
|
||||
{
|
||||
private const string ModuleName = "Files";
|
||||
|
||||
public const string CategoryMiscFile = "Misc File Functions";
|
||||
|
||||
public FileModule() : base(ModuleName)
|
||||
{
|
||||
RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current directory.
|
||||
/// </summary>
|
||||
/// <returns>The current directory.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> pwd
|
||||
/// # pwd
|
||||
/// out = "/code/kalk/tests"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("pwd", CategoryMiscFile)]
|
||||
public string CurrentDirectory()
|
||||
{
|
||||
return Environment.CurrentDirectory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the current directory to the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the directory to change.</param>
|
||||
/// <returns>The current directory or throws an exception if the directory does not exists</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> cd
|
||||
/// # cd
|
||||
/// out = "/code/kalk/tests"
|
||||
/// >>> mkdir "testdir"
|
||||
/// >>> cd "testdir"
|
||||
/// # cd("testdir")
|
||||
/// out = "/code/kalk/tests/testdir"
|
||||
/// >>> cd ".."
|
||||
/// # cd("..")
|
||||
/// out = "/code/kalk/tests"
|
||||
/// >>> rmdir "testdir"
|
||||
/// >>> dir_exists "testdir"
|
||||
/// # dir_exists("testdir")
|
||||
/// out = false
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("cd", CategoryMiscFile)]
|
||||
public string ChangeDirectory(string path = null)
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
if (!DirectoryExists(path)) throw new ArgumentException($"The folder `{path}` does not exists", nameof(path));
|
||||
Environment.CurrentDirectory = Path.Combine(Environment.CurrentDirectory, path);
|
||||
}
|
||||
|
||||
return CurrentDirectory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified file path exists on the disk.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to a file.</param>
|
||||
/// <returns>`true` if the specified file path exists on the disk.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> rm "test.txt"
|
||||
/// >>> file_exists "test.txt"
|
||||
/// # file_exists("test.txt")
|
||||
/// out = false
|
||||
/// >>> save_text("content", "test.txt")
|
||||
/// >>> file_exists "test.txt"
|
||||
/// # file_exists("test.txt")
|
||||
/// out = true
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("file_exists", CategoryMiscFile)]
|
||||
public KalkBool FileExists(string path)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
return Engine.FileService.FileExists(Path.Combine(Environment.CurrentDirectory, path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified directory path exists on the disk.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to a directory.</param>
|
||||
/// <returns>`true` if the specified directory path exists on the disk.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> mkdir "testdir"
|
||||
/// >>> dir_exists "testdir"
|
||||
/// # dir_exists("testdir")
|
||||
/// out = true
|
||||
/// >>> rmdir "testdir"
|
||||
/// >>> dir_exists "testdir"
|
||||
/// # dir_exists("testdir")
|
||||
/// out = false
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("dir_exists", CategoryMiscFile)]
|
||||
public KalkBool DirectoryExists(string path)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
return Engine.FileService.DirectoryExists(Path.Combine(Environment.CurrentDirectory, path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List files and directories from the specified path or the current directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The specified directory or the current directory if not specified.</param>
|
||||
/// <param name="recursive">A boolean to perform a recursive list. Default is `false`.</param>
|
||||
/// <returns>An enumeration of the files and directories.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> mkdir "testdir"
|
||||
/// >>> cd "testdir"
|
||||
/// # cd("testdir")
|
||||
/// out = "/code/kalk/tests/testdir"
|
||||
/// >>> mkdir "subdir"
|
||||
/// >>> save_text("content", "file.txt")
|
||||
/// >>> dir "."
|
||||
/// # dir(".")
|
||||
/// out = ["./file.txt", "./subdir"]
|
||||
/// >>> save_text("content", "subdir/file2.txt")
|
||||
/// >>> dir(".", true)
|
||||
/// # dir(".", true)
|
||||
/// out = ["./file.txt", "./subdir", "./subdir/file2.txt"]
|
||||
/// >>> cd ".."
|
||||
/// # cd("..")
|
||||
/// out = "/code/kalk/tests"
|
||||
/// >>> rmdir("testdir", true)
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("dir", CategoryMiscFile)]
|
||||
public IEnumerable DirectoryListing(string path = null, bool recursive = false)
|
||||
{
|
||||
string search = "*";
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
var wildcardIndex = path.IndexOf('*', StringComparison.OrdinalIgnoreCase);
|
||||
if (wildcardIndex >= 0)
|
||||
{
|
||||
search = path.Substring(wildcardIndex);
|
||||
path = path.Substring(0, wildcardIndex);
|
||||
}
|
||||
}
|
||||
|
||||
var fullDir = string.IsNullOrEmpty(path) ? Environment.CurrentDirectory : Path.Combine(Environment.CurrentDirectory, path);
|
||||
if (!Engine.FileService.DirectoryExists(fullDir)) throw new ArgumentException($"Directory `{fullDir}` not found.");
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
path = ".";
|
||||
}
|
||||
return new ScriptRange(Engine.FileService.EnumerateFileSystemEntries(path, search, recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file from the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the file to delete.</param>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> rm "test.txt"
|
||||
/// >>> file_exists "test.txt"
|
||||
/// # file_exists("test.txt")
|
||||
/// out = false
|
||||
/// >>> save_text("content", "test.txt")
|
||||
/// >>> file_exists "test.txt"
|
||||
/// # file_exists("test.txt")
|
||||
/// out = true
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("rm", CategoryMiscFile)]
|
||||
public void RemoveFile(string path)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
if (FileExists(path))
|
||||
{
|
||||
Engine.FileService.FileDelete(Path.Combine(Environment.CurrentDirectory, path));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a directory at the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the directory to create.</param>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> mkdir "testdir"
|
||||
/// >>> dir_exists "testdir"
|
||||
/// # dir_exists("testdir")
|
||||
/// out = true
|
||||
/// >>> rmdir "testdir"
|
||||
/// >>> dir_exists "testdir"
|
||||
/// # dir_exists("testdir")
|
||||
/// out = false
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("mkdir", CategoryMiscFile)]
|
||||
public void CreateDirectory(string path)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
if (DirectoryExists(path)) return;
|
||||
Engine.FileService.DirectoryCreate(Path.Combine(Environment.CurrentDirectory, path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the directory at the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the directory to delete.</param>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> mkdir "testdir"
|
||||
/// >>> dir_exists "testdir"
|
||||
/// # dir_exists("testdir")
|
||||
/// out = true
|
||||
/// >>> rmdir "testdir"
|
||||
/// >>> dir_exists "testdir"
|
||||
/// # dir_exists("testdir")
|
||||
/// out = false
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("rmdir", CategoryMiscFile)]
|
||||
public void RemoveDirectory(string path, bool recursive = true)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
if (DirectoryExists(path))
|
||||
{
|
||||
Engine.FileService.DirectoryDelete(Path.Combine(Environment.CurrentDirectory, path), recursive);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the specified file as text.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to a file to load as text.</param>
|
||||
/// <param name="encoding">The encoding of the file. Default is "utf-8"</param>
|
||||
/// <returns>The file loaded as a string.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> load_text "test.csv"
|
||||
/// # load_text("test.csv")
|
||||
/// out = "a,b,c\n1,2,3\n4,5,6"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("load_text", CategoryMiscFile)]
|
||||
public string LoadText(string path, string encoding = KalkConfig.DefaultEncoding)
|
||||
{
|
||||
var fullPath = AssertReadFile(path);
|
||||
var encoder = GetEncoding(encoding);
|
||||
using var stream = Engine.FileService.FileOpen(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return new StreamReader(stream, encoder).ReadToEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the specified file as binary.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to a file to load as binary.</param>
|
||||
/// <returns>The file loaded as a a byte buffer.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> load_bytes "test.csv"
|
||||
/// # load_bytes("test.csv")
|
||||
/// out = bytebuffer([97, 44, 98, 44, 99, 10, 49, 44, 50, 44, 51, 10, 52, 44, 53, 44, 54])
|
||||
/// >>> ascii out
|
||||
/// # ascii(out)
|
||||
/// out = "a,b,c\n1,2,3\n4,5,6"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("load_bytes", CategoryMiscFile)]
|
||||
public KalkNativeBuffer LoadBytes(string path)
|
||||
{
|
||||
var fullPath = AssertReadFile(path);
|
||||
using var stream = Engine.FileService.FileOpen(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
var buffer = new KalkNativeBuffer((int)stream.Length);
|
||||
stream.Read(buffer.AsSpan());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load each lines from the specified file path.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to a file to load lines from.</param>
|
||||
/// <param name="encoding">The encoding of the file. Default is "utf-8"</param>
|
||||
/// <returns>An enumeration on the lines.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> load_lines "test.csv"
|
||||
/// # load_lines("test.csv")
|
||||
/// out = ["a,b,c", "1,2,3", "4,5,6"]
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("load_lines", CategoryMiscFile)]
|
||||
public ScriptRange LoadLines(string path, string encoding = KalkConfig.DefaultEncoding)
|
||||
{
|
||||
var fullPath = AssertReadFile(path);
|
||||
var encoder = GetEncoding(encoding);
|
||||
return new ScriptRange(new LineReader(() =>
|
||||
{
|
||||
var stream = Engine.FileService.FileOpen(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return new StreamReader(stream, encoder);
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves an array of data as string to the specified files.
|
||||
/// </summary>
|
||||
/// <param name="lines">An array of data.</param>
|
||||
/// <param name="path">Path to the file to save the lines to.</param>
|
||||
/// <param name="encoding">The encoding of the file. Default is "utf-8"</param>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> save_lines(1..10, "lines.txt")
|
||||
/// >>> load_lines("lines.txt")
|
||||
/// # load_lines("lines.txt")
|
||||
/// out = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("save_lines", CategoryMiscFile)]
|
||||
public object SaveLines(IEnumerable lines, string path, string encoding = KalkConfig.DefaultEncoding)
|
||||
{
|
||||
if (lines == null) throw new ArgumentNullException(nameof(lines));
|
||||
var fullPath = AssertWriteFile(path);
|
||||
var encoder = GetEncoding(encoding);
|
||||
using var stream = Engine.FileService.FileOpen(fullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
|
||||
using var writer = new StreamWriter(stream, encoder);
|
||||
foreach (var lineObj in lines)
|
||||
{
|
||||
var line = Engine.ObjectToString(lineObj);
|
||||
writer.WriteLine(line);
|
||||
}
|
||||
// return object to allow this function to be used in a pipe
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a text to the specified file path.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to save.</param>
|
||||
/// <param name="path">Path to the file to save the text to.</param>
|
||||
/// <param name="encoding">The encoding of the file. Default is "utf-8"</param>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> save_text("Hello World!", "test.txt")
|
||||
/// >>> load_text("test.txt")
|
||||
/// # load_text("test.txt")
|
||||
/// out = "Hello World!"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("save_text", CategoryMiscFile)]
|
||||
public object SaveText(string text, string path, string encoding = KalkConfig.DefaultEncoding)
|
||||
{
|
||||
var fullPath = AssertWriteFile(path);
|
||||
var encoder = GetEncoding(encoding);
|
||||
using var stream = Engine.FileService.FileOpen(fullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
|
||||
using var writer = new StreamWriter(stream, encoder);
|
||||
text ??= string.Empty;
|
||||
writer.Write(text);
|
||||
// return object to allow this function to be used in a pipe
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a byte buffer to the specified file path.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to save.</param>
|
||||
/// <param name="path">Path to the file to save the data to.</param>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> utf8("Hello World!")
|
||||
/// # utf8("Hello World!")
|
||||
/// out = bytebuffer([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
|
||||
/// >>> save_bytes(out, "test.bin")
|
||||
/// >>> load_bytes("test.bin")
|
||||
/// # load_bytes("test.bin")
|
||||
/// out = bytebuffer([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
|
||||
/// >>> utf8(out)
|
||||
/// # utf8(out)
|
||||
/// out = "Hello World!"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("save_bytes", CategoryMiscFile)]
|
||||
public object SaveBytes(IEnumerable data, string path)
|
||||
{
|
||||
var fullPath = AssertWriteFile(path);
|
||||
using var stream = Engine.FileService.FileOpen(fullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
|
||||
|
||||
switch (data)
|
||||
{
|
||||
case ScriptRange range when range.Values is byte[] byteBuffer:
|
||||
stream.Write(byteBuffer, 0, byteBuffer.Length);
|
||||
break;
|
||||
case KalkNativeBuffer byteBuffer:
|
||||
{
|
||||
stream.Write(byteBuffer.AsSpan());
|
||||
break;
|
||||
}
|
||||
case ScriptArray<byte> scriptByteArray:
|
||||
for (int i = 0; i < scriptByteArray.Count; i++)
|
||||
{
|
||||
stream.WriteByte(scriptByteArray[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
foreach (var item in data)
|
||||
{
|
||||
var b = Engine.ToObject<byte>(0, item);
|
||||
stream.WriteByte(b);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// return object to allow this function to be used in a pipe
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public string AssertWriteFile(string path)
|
||||
{
|
||||
return AssertFile(path, true);
|
||||
}
|
||||
|
||||
public string AssertReadFile(string path)
|
||||
{
|
||||
return AssertFile(path, false);
|
||||
}
|
||||
|
||||
private string AssertFile(string path, bool toWrite)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
var fullPath = Path.Combine(Environment.CurrentDirectory, path);
|
||||
if (toWrite)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(fullPath);
|
||||
if (!Engine.FileService.DirectoryExists(directory))
|
||||
{
|
||||
try
|
||||
{
|
||||
Engine.FileService.DirectoryCreate(directory);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ArgumentException($"Cannot write to file {path}. Cannot create directory {directory}. Reason: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Engine.FileService.FileExists(fullPath)) throw new ArgumentException($"File {path} was not found.");
|
||||
}
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
private static Encoding GetEncoding(string encoding)
|
||||
{
|
||||
if (encoding == null) throw new ArgumentNullException(nameof(encoding));
|
||||
try
|
||||
{
|
||||
switch (encoding)
|
||||
{
|
||||
case "utf-8": return Encoding.UTF8;
|
||||
case "utf-32": return Encoding.UTF32;
|
||||
case "ascii": return Encoding.ASCII;
|
||||
default:
|
||||
return Encoding.GetEncoding(encoding);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ArgumentException($"Invalid encoding name `{encoding}`. Reason: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Kalk.Core.Modules.HardwareIntrinsics.Arm
|
||||
{
|
||||
public partial class AdvSimdIntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public AdvSimdIntrinsicsModule() : base("ARM") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class AdvSimdArm64IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public AdvSimdArm64IntrinsicsModule() : base("ARM64") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class AesIntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public AesIntrinsicsModule() : base("ARM AES") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Crc32IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Crc32IntrinsicsModule() : base("ARM Crc32") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Crc32Arm64IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Crc32Arm64IntrinsicsModule() : base("ARM64 Crc32") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sha1IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sha1IntrinsicsModule() : base("ARM Sha1") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sha256IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sha256IntrinsicsModule() : base("ARM Sha256") => RegisterFunctionsAuto();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
using Kalk.Core.Modules.HardwareIntrinsics;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// Module with CPU Hardware intrinsics.
|
||||
/// </summary>
|
||||
[KalkExportModule(ModuleName)]
|
||||
public partial class HardwareIntrinsicsModule : KalkModuleWithFunctions
|
||||
{
|
||||
private const string ModuleName = "HardwareIntrinsics";
|
||||
private const string CategoryIntrinsics = "Vector Hardware Intrinsics";
|
||||
|
||||
public HardwareIntrinsicsModule() : base(ModuleName)
|
||||
{
|
||||
RegisterDocumentationAuto();
|
||||
}
|
||||
|
||||
protected override void Import()
|
||||
{
|
||||
// X86
|
||||
if (System.Runtime.Intrinsics.X86.Sse.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.SseIntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Sse.X64.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.SseX64IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Sse2.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Sse2IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Sse2.X64.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Sse2X64IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Sse3.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Sse3IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Ssse3.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Ssse3IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Sse41.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Sse41IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Sse41.X64.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Sse41X64IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Sse42.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Sse42IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Sse42.X64.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Sse42X64IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Avx.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.AvxIntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Avx2.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Avx2IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Aes.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.AesIntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Bmi1.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Bmi1IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Bmi1.X64.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Bmi1X64IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Bmi2.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Bmi2IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.X86.Bmi2.X64.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.X86.Bmi2X64IntrinsicsModule>();
|
||||
}
|
||||
// Arm
|
||||
if (System.Runtime.Intrinsics.Arm.AdvSimd.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.Arm.AdvSimdIntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.Arm.AdvSimd.Arm64.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.Arm.AdvSimdArm64IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.Arm.Aes.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.Arm.AesIntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.Arm.Crc32.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.Arm.Crc32IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.Arm.Crc32.Arm64.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.Arm.Crc32Arm64IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.Arm.Sha1.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.Arm.Sha1IntrinsicsModule>();
|
||||
}
|
||||
if (System.Runtime.Intrinsics.Arm.Sha256.IsSupported)
|
||||
{
|
||||
DynamicRegister<HardwareIntrinsics.Arm.Sha256IntrinsicsModule>();
|
||||
}
|
||||
}
|
||||
private void DynamicRegister<TModule>() where TModule : IntrinsicsModuleBase, new()
|
||||
{
|
||||
var module = new TModule();
|
||||
module.Initialize(Engine);
|
||||
module.InternalImport();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
namespace Kalk.Core.Modules.HardwareIntrinsics.X86
|
||||
{
|
||||
public partial class SseIntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public SseIntrinsicsModule() : base("SSE") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class SseX64IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public SseX64IntrinsicsModule() : base("SSE (x64)") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sse2IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sse2IntrinsicsModule() : base("SSE2") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sse2X64IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sse2X64IntrinsicsModule() : base("SSE2 (x64)") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sse3IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sse3IntrinsicsModule() : base("SSE3") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sse41IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sse41IntrinsicsModule() : base("SSE4.1") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sse41X64IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sse41X64IntrinsicsModule() : base("SSE4.1 (x64)") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sse42IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sse42IntrinsicsModule() : base("SSE4.2") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Sse42X64IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Sse42X64IntrinsicsModule() : base("SSE4.2 (x64)") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Ssse3IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Ssse3IntrinsicsModule() : base("SSSE3") => RegisterFunctionsAuto();
|
||||
}
|
||||
public partial class AvxIntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public AvxIntrinsicsModule() : base("AVX") => RegisterFunctionsAuto();
|
||||
}
|
||||
public partial class Avx2IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Avx2IntrinsicsModule() : base("AVX2") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class AesIntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public AesIntrinsicsModule() : base("AES") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Bmi1IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Bmi1IntrinsicsModule() : base("BMI1") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Bmi1X64IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Bmi1X64IntrinsicsModule() : base("BMI1 (x64)") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Bmi2IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Bmi2IntrinsicsModule() : base("BMI2") => RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
public partial class Bmi2X64IntrinsicsModule : IntrinsicsModuleBase
|
||||
{
|
||||
public Bmi2X64IntrinsicsModule() : base("BMI2 (x64)") => RegisterFunctionsAuto();
|
||||
}
|
||||
}
|
||||
32389
Kalk/Kalk.Core/Modules/HardwareIntrinsics/Intrinsics.generated.cs
Normal file
32389
Kalk/Kalk.Core/Modules/HardwareIntrinsics/Intrinsics.generated.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core.Modules.HardwareIntrinsics
|
||||
{
|
||||
|
||||
public abstract class IntrinsicsModuleBase : KalkModuleWithFunctions
|
||||
{
|
||||
public const string CategoryIntrinsics = "Vector Hardware Intrinsics";
|
||||
|
||||
protected IntrinsicsModuleBase(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
protected void ProcessAction<TArg1Base, TArg1>(object arg1, Action<TArg1> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1);
|
||||
func(nativeArg1);
|
||||
}
|
||||
|
||||
protected void ProcessAction<TArg1Base, TArg1, TArg2Base, TArg2>(object arg1, object arg2, Action<TArg1, TArg2> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TArg2Base : unmanaged where TArg2: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1);
|
||||
var nativeArg2 = ToArg<TArg2Base, TArg2>(1, arg2);
|
||||
func(nativeArg1, nativeArg2);
|
||||
}
|
||||
|
||||
protected void ProcessAction<TArg1Base, TArg1, TArg2Base, TArg2>(object arg1, int arg1Align, object arg2, Action<TArg1, TArg2> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TArg2Base : unmanaged where TArg2: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1, arg1Align);
|
||||
var nativeArg2 = ToArg<TArg2Base, TArg2>(1, arg2);
|
||||
func(nativeArg1, nativeArg2);
|
||||
}
|
||||
|
||||
protected void ProcessAction<TArg1Base, TArg1, TArg2Base, TArg2, TArg3Base, TArg3>(object arg1, object arg2, object arg3, Action<TArg1, TArg2, TArg3> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TArg2Base : unmanaged where TArg2: unmanaged
|
||||
where TArg3Base : unmanaged where TArg3: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1);
|
||||
var nativeArg2 = ToArg<TArg2Base, TArg2>(1, arg2);
|
||||
var nativeArg3 = ToArg<TArg3Base, TArg3>(2, arg3);
|
||||
func(nativeArg1, nativeArg2, nativeArg3);
|
||||
}
|
||||
|
||||
protected object ProcessFunc<TArg1Base, TArg1, TResultBase, TResult>(object arg1, Func<TArg1, TResult> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TResultBase : unmanaged where TResult: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1);
|
||||
var nativeResult = func(nativeArg1);
|
||||
return ToResult<TResultBase, TResult>(nativeResult);
|
||||
}
|
||||
|
||||
protected object ProcessFunc<TArg1Base, TArg1, TResultBase, TResult>(object arg1, int arg1Align, Func<TArg1, TResult> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TResultBase : unmanaged where TResult: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1, arg1Align);
|
||||
var nativeResult = func(nativeArg1);
|
||||
return ToResult<TResultBase, TResult>(nativeResult);
|
||||
}
|
||||
|
||||
protected object ProcessFunc<TArg1Base, TArg1, TArg2Base, TArg2, TResultBase, TResult>(object arg1, object arg2, Func<TArg1, TArg2, TResult> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TArg2Base : unmanaged where TArg2: unmanaged
|
||||
where TResultBase : unmanaged where TResult: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1);
|
||||
var nativeArg2 = ToArg<TArg2Base, TArg2>(1, arg2);
|
||||
var nativeResult = func(nativeArg1, nativeArg2);
|
||||
return ToResult<TResultBase, TResult>(nativeResult);
|
||||
}
|
||||
|
||||
protected object ProcessFunc<TArg1Base, TArg1, TArg2Base, TArg2, TArg3Base, TArg3, TResultBase, TResult>(object arg1, object arg2, object arg3, Func<TArg1, TArg2, TArg3, TResult> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TArg2Base : unmanaged where TArg2: unmanaged
|
||||
where TArg3Base : unmanaged where TArg3: unmanaged
|
||||
where TResultBase : unmanaged where TResult: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1);
|
||||
var nativeArg2 = ToArg<TArg2Base, TArg2>(1, arg2);
|
||||
var nativeArg3 = ToArg<TArg3Base, TArg3>(2, arg3);
|
||||
var nativeResult = func(nativeArg1, nativeArg2, nativeArg3);
|
||||
return ToResult<TResultBase, TResult>(nativeResult);
|
||||
}
|
||||
|
||||
protected object ProcessFunc<TArg1Base, TArg1, TArg2Base, TArg2, TArg3Base, TArg3, TArg4Base, TArg4, TResultBase, TResult>(object arg1, object arg2, object arg3, object arg4, Func<TArg1, TArg2, TArg3, TArg4, TResult> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TArg2Base : unmanaged where TArg2: unmanaged
|
||||
where TArg3Base : unmanaged where TArg3: unmanaged
|
||||
where TArg4Base : unmanaged where TArg4: unmanaged
|
||||
where TResultBase : unmanaged where TResult: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1);
|
||||
var nativeArg2 = ToArg<TArg2Base, TArg2>(1, arg2);
|
||||
var nativeArg3 = ToArg<TArg3Base, TArg3>(2, arg3);
|
||||
var nativeArg4 = ToArg<TArg4Base, TArg4>(3, arg4);
|
||||
var nativeResult = func(nativeArg1, nativeArg2, nativeArg3, nativeArg4);
|
||||
return ToResult<TResultBase, TResult>(nativeResult);
|
||||
}
|
||||
|
||||
protected object ProcessFunc<TArg1Base, TArg1, TArg2Base, TArg2, TArg3Base, TArg3, TArg4Base, TArg4, TArg5Base, TArg5, TResultBase, TResult>(object arg1, object arg2, object arg3, object arg4, object arg5, Func<TArg1, TArg2, TArg3, TArg4, TArg5, TResult> func)
|
||||
where TArg1Base : unmanaged where TArg1: unmanaged
|
||||
where TArg2Base : unmanaged where TArg2: unmanaged
|
||||
where TArg3Base : unmanaged where TArg3: unmanaged
|
||||
where TArg4Base : unmanaged where TArg4: unmanaged
|
||||
where TArg5Base : unmanaged where TArg5: unmanaged
|
||||
where TResultBase : unmanaged where TResult: unmanaged
|
||||
{
|
||||
var nativeArg1 = ToArg<TArg1Base, TArg1>(0, arg1);
|
||||
var nativeArg2 = ToArg<TArg2Base, TArg2>(1, arg2);
|
||||
var nativeArg3 = ToArg<TArg3Base, TArg3>(2, arg3);
|
||||
var nativeArg4 = ToArg<TArg4Base, TArg4>(3, arg4);
|
||||
var nativeArg5 = ToArg<TArg5Base, TArg5>(4, arg5);
|
||||
var nativeResult = func(nativeArg1, nativeArg2, nativeArg3, nativeArg4, nativeArg5);
|
||||
return ToResult<TResultBase, TResult>(nativeResult);
|
||||
}
|
||||
|
||||
private T ToArg<TBase, T>(int argIndex, object value, int align = 0) where T : unmanaged where TBase: unmanaged
|
||||
{
|
||||
if (typeof(T) == typeof(IntPtr))
|
||||
{
|
||||
var buffer = value as KalkNativeBuffer;
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ScriptArgumentException(argIndex, "Expecting a byte buffer. Use malloc(size) to pass data to this argument.");
|
||||
}
|
||||
|
||||
var ptr = buffer.GetPointer();
|
||||
if (align != 0)
|
||||
{
|
||||
var remainder = (long) ptr & (long) align - 1;
|
||||
if (remainder != 0)
|
||||
{
|
||||
throw new ScriptArgumentException(argIndex, $"Invalid memory alignment. Expecting an alignment on {align} bytes, but the bytebuffer offset is off by {remainder} bytes");
|
||||
}
|
||||
}
|
||||
var rawPtr = Unsafe.As<IntPtr, T>(ref ptr);
|
||||
return rawPtr;
|
||||
}
|
||||
|
||||
var targetSize = Unsafe.SizeOf<T>();
|
||||
var baseElementSize = Unsafe.SizeOf<TBase>();
|
||||
var dimension = targetSize / baseElementSize;
|
||||
if (dimension == 1)
|
||||
{
|
||||
// Handle Vector64
|
||||
if (typeof(System.Runtime.Intrinsics.Vector64<long>) == typeof(T))
|
||||
{
|
||||
var tValue = Engine.ToObject<long>(argIndex, value);
|
||||
return (T)(object)(System.Runtime.Intrinsics.Vector64.Create(tValue));
|
||||
}
|
||||
if (typeof(System.Runtime.Intrinsics.Vector64<ulong>) == typeof(T))
|
||||
{
|
||||
var tValue = Engine.ToObject<ulong>(argIndex, value);
|
||||
return (T)(object)(System.Runtime.Intrinsics.Vector64.Create(tValue));
|
||||
}
|
||||
if (typeof(System.Runtime.Intrinsics.Vector64<double>) == typeof(T))
|
||||
{
|
||||
var tValue = Engine.ToObject<double>(argIndex, value);
|
||||
return (T)(object)(System.Runtime.Intrinsics.Vector64.Create(tValue));
|
||||
}
|
||||
return Engine.ToObject<T>(argIndex, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(dimension > 1);
|
||||
Span<TBase> elements = stackalloc TBase[dimension];
|
||||
if (value is KalkVector vec)
|
||||
{
|
||||
var span = vec.AsSpan();
|
||||
var minSize = Math.Min(targetSize, span.Length);
|
||||
span = span.Slice(0, minSize);
|
||||
span.CopyTo(MemoryMarshal.Cast<TBase, byte>(elements));
|
||||
}
|
||||
else
|
||||
{
|
||||
var leftValueT = Engine.ToObject<TBase>(argIndex, value);
|
||||
for (int i = 0; i < dimension; i++)
|
||||
{
|
||||
elements[i] = leftValueT;
|
||||
}
|
||||
}
|
||||
return Unsafe.As<TBase, T>(ref elements[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private object ToResult<TBase, T>(T result) where T: unmanaged where TBase: unmanaged
|
||||
{
|
||||
var targetSize = Unsafe.SizeOf<T>();
|
||||
var baseElementSize = Unsafe.SizeOf<TBase>();
|
||||
var dimension = targetSize / baseElementSize;
|
||||
var span = MemoryMarshal.Cast<T, TBase>(MemoryMarshal.CreateSpan(ref result, 1));
|
||||
if (dimension == 1)
|
||||
{
|
||||
return span[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
var vector = new KalkVector<TBase>(dimension);
|
||||
for (int i = 0; i < dimension; i++)
|
||||
{
|
||||
vector[i] = span[i];
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1692
Kalk/Kalk.Core/Modules/MathModule.cs
Normal file
1692
Kalk/Kalk.Core/Modules/MathModule.cs
Normal file
File diff suppressed because it is too large
Load Diff
1055
Kalk/Kalk.Core/Modules/MemoryModule.cs
Normal file
1055
Kalk/Kalk.Core/Modules/MemoryModule.cs
Normal file
File diff suppressed because it is too large
Load Diff
1582
Kalk/Kalk.Core/Modules/MiscModule.cs
Normal file
1582
Kalk/Kalk.Core/Modules/MiscModule.cs
Normal file
File diff suppressed because it is too large
Load Diff
31
Kalk/Kalk.Core/Modules/StandardUnitsModule.cs
Normal file
31
Kalk/Kalk.Core/Modules/StandardUnitsModule.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// Modules that contains standard units.
|
||||
/// </summary>
|
||||
[KalkExportModule(ModuleName)]
|
||||
public partial class StandardUnitsModule : KalkModuleWithFunctions
|
||||
{
|
||||
private const string ModuleName = "StandardUnits";
|
||||
public StandardUnitsModule() : base(ModuleName)
|
||||
{
|
||||
RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Import()
|
||||
{
|
||||
base.Import();
|
||||
|
||||
var countBeforeImport = Engine.Units.Count;
|
||||
Engine.LoadSystemFileFromResource("units.kalk");
|
||||
var deltaCount = Engine.Units.Count - countBeforeImport;
|
||||
Engine.WriteHighlightLine($"# {deltaCount} units successfully imported from module `{Name}`.");
|
||||
}
|
||||
}
|
||||
}
|
||||
385
Kalk/Kalk.Core/Modules/StringModule.cs
Normal file
385
Kalk/Kalk.Core/Modules/StringModule.cs
Normal file
@@ -0,0 +1,385 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Scriban.Functions;
|
||||
using Scriban.Runtime;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// Modules that provides string functions (e.g `upcase`, `downcase`, `regex_escape`...).
|
||||
/// </summary>
|
||||
[KalkExportModule(ModuleName)]
|
||||
public partial class StringModule : KalkModuleWithFunctions
|
||||
{
|
||||
private const string ModuleName = "Strings";
|
||||
public const string CategoryString = "Text Functions";
|
||||
|
||||
public StringModule() : base(ModuleName)
|
||||
{
|
||||
RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
/// <summary>Escapes a string with escape characters.</summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The two strings concatenated</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "Hel\tlo\n\"W\\orld" |> escape
|
||||
/// # "Hel\tlo\n\"W\\orld" |> escape
|
||||
/// out = "Hel\\tlo\\n\\\"W\\\\orld"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("escape", CategoryString)]
|
||||
public string StringEscape(string text) => StringFunctions.Escape(text);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the first character of the passed string to a upper case character.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The capitalized input string</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "test" |> capitalize
|
||||
/// # "test" |> capitalize
|
||||
/// out = "Test"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("capitalize", CategoryString)]
|
||||
public string StringCapitalize(string text) => StringFunctions.Capitalize(text);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the first character of each word in the passed string to a upper case character.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The capitalized input string</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "This is easy" |> capitalize_words
|
||||
/// # "This is easy" |> capitalize_words
|
||||
/// out = "This Is Easy"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("capitalize_words", CategoryString)]
|
||||
public string StringCapitalizeWords(string text) => StringFunctions.Capitalizewords(text);
|
||||
|
||||
/// <summary>Converts the string to lower case.</summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string lower case</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "TeSt" |> downcase
|
||||
/// # "TeSt" |> downcase
|
||||
/// out = "test"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("downcase", CategoryString)]
|
||||
public string StringDowncase(string text) => StringFunctions.Downcase(text);
|
||||
|
||||
/// <summary>Converts the string to uppercase</summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string upper case</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "test" |> upcase
|
||||
/// # "test" |> upcase
|
||||
/// out = "TEST"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("upcase", CategoryString)]
|
||||
public string StringUpcase(string text) => StringFunctions.Upcase(text);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a boolean indicating whether the input string ends with the specified string `value`.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <param name="end">The string to look for</param>
|
||||
/// <returns><c>true</c> if `text` ends with the specified string `value`</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "This is easy" |> endswith "easy"
|
||||
/// # "This is easy" |> endswith("easy")
|
||||
/// out = true
|
||||
/// >>> "This is easy" |> endswith "none"
|
||||
/// # "This is easy" |> endswith("none")
|
||||
/// out = false
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("endswith", CategoryString)]
|
||||
public KalkBool StringEndsWith(string text, string end) => StringFunctions.EndsWith(text, end);
|
||||
|
||||
/// <summary>Returns a url handle from the input string.</summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>A url handle</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> '100% M @ Ms!!!' |> handleize
|
||||
/// # '100% M @ Ms!!!' |> handleize
|
||||
/// out = "100-m-ms"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("handleize", CategoryString)]
|
||||
public string StringHandleize(string text) => StringFunctions.Handleize(text);
|
||||
|
||||
/// <summary>
|
||||
/// Removes any whitespace characters on the **left** side of the input string.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string without any left whitespace characters</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> ' too many spaces' |> lstrip
|
||||
/// # ' too many spaces' |> lstrip
|
||||
/// out = "too many spaces"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("lstrip", CategoryString)]
|
||||
public string StringLeftStrip(string text) => StringFunctions.LStrip(text);
|
||||
|
||||
/// <summary>
|
||||
/// Outputs the singular or plural version of a string based on the value of a number.
|
||||
/// </summary>
|
||||
/// <param name="number">The number to check</param>
|
||||
/// <param name="singular">The singular string to return if number is == 1</param>
|
||||
/// <param name="plural">The plural string to return if number is != 1</param>
|
||||
/// <returns>The singular or plural string based on number</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> 3 |> pluralize('product', 'products')
|
||||
/// # 3 |> pluralize('product', 'products')
|
||||
/// out = "products"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("pluralize", CategoryString)]
|
||||
public string StringPluralize(int number, string singular, string plural) => StringFunctions.Pluralize(number, singular, plural);
|
||||
|
||||
/// <summary>
|
||||
/// Removes any whitespace characters on the **right** side of the input string.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string without any left whitespace characters</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> ' too many spaces ' |> rstrip
|
||||
/// # ' too many spaces ' |> rstrip
|
||||
/// out = " too many spaces"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("rstrip", CategoryString)]
|
||||
public string StringRightStrip(string text) => StringFunctions.RStrip(text);
|
||||
|
||||
/// <summary>
|
||||
/// The `split` function takes on a substring as a parameter.
|
||||
/// The substring is used as a delimiter to divide a string into an array. You can output different parts of an array using `array` functions.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <param name="match">The string used to split the input `text` string</param>
|
||||
/// <returns>An enumeration of the substrings</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "Hi, how are you today?" |> split ' '
|
||||
/// # "Hi, how are you today?" |> split(' ')
|
||||
/// out = ["Hi,", "how", "are", "you", "today?"]
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("split", CategoryString)]
|
||||
public IEnumerable StringSplit(string text, string match) => StringFunctions.Split(text, match);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a boolean indicating whether the input string starts with the specified string `value`.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <param name="start">The string to look for</param>
|
||||
/// <returns><c>true</c> if `text` starts with the specified string `value`</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "This is easy" |> startswith "This"
|
||||
/// # "This is easy" |> startswith("This")
|
||||
/// out = true
|
||||
/// >>> "This is easy" |> startswith "easy"
|
||||
/// # "This is easy" |> startswith("easy")
|
||||
/// out = false
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("startswith", CategoryString)]
|
||||
public KalkBool StringStartsWith(string text, string start) => StringFunctions.StartsWith(text, start);
|
||||
|
||||
/// <summary>
|
||||
/// Removes any whitespace characters on the **left** and **right** side of the input string.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string without any left and right whitespace characters</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> ' too many spaces ' |> strip
|
||||
/// # ' too many spaces ' |> strip
|
||||
/// out = "too many spaces"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("strip", CategoryString)]
|
||||
public string StringStrip(string text) => StringFunctions.Strip(text);
|
||||
|
||||
/// <summary>Removes any line breaks/newlines from a string.</summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string without any breaks/newlines characters</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "This is a string.\r\n With \nanother \rstring" |> strip_newlines
|
||||
/// # "This is a string.\r\n With \nanother \rstring" |> strip_newlines
|
||||
/// out = "This is a string. With another string"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("strip_newlines", CategoryString)]
|
||||
public string StringStripNewlines(string text) => StringFunctions.StripNewlines(text);
|
||||
|
||||
/// <summary>
|
||||
/// Pads a string with leading spaces to a specified total length.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <param name="width">The number of characters in the resulting string</param>
|
||||
/// <returns>The input string padded</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "world" |> pad_left 10
|
||||
/// # "world" |> pad_left(10)
|
||||
/// out = " world"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("pad_left", CategoryString)]
|
||||
public string StringPadLeft(string text, int width) => StringFunctions.PadLeft(text, width);
|
||||
|
||||
/// <summary>
|
||||
/// Pads a string with trailing spaces to a specified total length.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <param name="width">The number of characters in the resulting string</param>
|
||||
/// <returns>The input string padded</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "hello" |> pad_right 10
|
||||
/// # "hello" |> pad_right(10)
|
||||
/// out = "hello "
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("pad_right", CategoryString)]
|
||||
public string StringPadRight(string text, int width) => StringFunctions.PadRight(text, width);
|
||||
|
||||
/// <summary>
|
||||
/// Escapes a minimal set of characters (`\`, `*`, `+`, `?`, `|`, `{`, `[`, `(`,`)`, `^`, `$`,`.`, `#`, and white space)
|
||||
/// by replacing them with their escape codes.
|
||||
/// This instructs the regular expression engine to interpret these characters literally rather than as metacharacters.
|
||||
/// </summary>
|
||||
/// <param name="text">The input string that contains the text to convert.</param>
|
||||
/// <returns>A string of characters with metacharacters converted to their escaped form.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "(abc.*)" |> regex_escape
|
||||
/// # "(abc.*)" |> regex_escape
|
||||
/// out = "\\(abc\\.\\*\\)"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("regex_escape", CategoryString)]
|
||||
public string RegexEscape(string text) => RegexFunctions.Escape(text);
|
||||
|
||||
/// <summary>
|
||||
/// Searches an input string for a substring that matches a regular expression pattern and returns an array with the match occurences.
|
||||
/// </summary>
|
||||
/// <param name="text">The string to search for a match.</param>
|
||||
/// <param name="pattern">The regular expression pattern to match.</param>
|
||||
/// <param name="options">A string with regex options, that can contain the following option characters (default is `null`):
|
||||
/// - `i`: Specifies case-insensitive matching.
|
||||
/// - `m`: Multiline mode. Changes the meaning of `^` and `$` so they match at the beginning and end, respectively, of any line, and not just the beginning and end of the entire string.
|
||||
/// - `s`: Specifies single-line mode. Changes the meaning of the dot `.` so it matches every character (instead of every character except `\n`).
|
||||
/// - `x`: Eliminates unescaped white space from the pattern and enables comments marked with `#`.
|
||||
/// </param>
|
||||
/// <returns>An array that contains all the match groups. The first group contains the entire match. The other elements contain regex matched groups `(..)`. An empty array returned means no match.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "this is a text123" |> regex_match `(\w+) a ([a-z]+\d+)`
|
||||
/// # "this is a text123" |> regex_match(`(\w+) a ([a-z]+\d+)`)
|
||||
/// out = ["is a text123", "is", "text123"]
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("regex_match", CategoryString)]
|
||||
public ScriptArray RegexMatch(string text, string pattern, string options = null) => RegexFunctions.Match(Engine, text, pattern, options);
|
||||
|
||||
/// <summary>
|
||||
/// Searches an input string for multiple substrings that matches a regular expression pattern and returns an array with the match occurences.
|
||||
/// </summary>
|
||||
/// <param name="text">The string to search for a match.</param>
|
||||
/// <param name="pattern">The regular expression pattern to match.</param>
|
||||
/// <param name="options">A string with regex options, that can contain the following option characters (default is `null`):
|
||||
/// - `i`: Specifies case-insensitive matching.
|
||||
/// - `m`: Multiline mode. Changes the meaning of `^` and `$` so they match at the beginning and end, respectively, of any line, and not just the beginning and end of the entire string.
|
||||
/// - `s`: Specifies single-line mode. Changes the meaning of the dot `.` so it matches every character (instead of every character except `\n`).
|
||||
/// - `x`: Eliminates unescaped white space from the pattern and enables comments marked with `#`.
|
||||
/// </param>
|
||||
/// <returns>An array of matches that contains all the match groups. The first group contains the entire match. The other elements contain regex matched groups `(..)`. An empty array returned means no match.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "this is a text123" |> regex_matches `(\w+)`
|
||||
/// # "this is a text123" |> regex_matches(`(\w+)`)
|
||||
/// out = [["this", "this"], ["is", "is"], ["a", "a"], ["text123", "text123"]]
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("regex_matches", CategoryString)]
|
||||
public ScriptArray RegexMatches(string text, string pattern, string options = null) => RegexFunctions.Matches(Engine, text, pattern, options);
|
||||
|
||||
/// <summary>
|
||||
/// In a specified input string, replaces strings that match a regular expression pattern with a specified replacement string.
|
||||
/// </summary>
|
||||
/// <param name="text">The string to search for a match.</param>
|
||||
/// <param name="pattern">The regular expression pattern to match.</param>
|
||||
/// <param name="replace">The replacement string.</param>
|
||||
/// <param name="options">A string with regex options, that can contain the following option characters (default is `null`):
|
||||
/// - `i`: Specifies case-insensitive matching.
|
||||
/// - `m`: Multiline mode. Changes the meaning of `^` and `$` so they match at the beginning and end, respectively, of any line, and not just the beginning and end of the entire string.
|
||||
/// - `s`: Specifies single-line mode. Changes the meaning of the dot `.` so it matches every character (instead of every character except `\n`).
|
||||
/// - `x`: Eliminates unescaped white space from the pattern and enables comments marked with `#`.
|
||||
/// </param>
|
||||
/// <returns>A new string that is identical to the input string, except that the replacement string takes the place of each matched string. If pattern is not matched in the current instance, the method returns the current instance unchanged.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "abbbbcccd" |> regex_replace("b+c+","-Yo-")
|
||||
/// # "abbbbcccd" |> regex_replace("b+c+", "-Yo-")
|
||||
/// out = "a-Yo-d"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("regex_replace", CategoryString)]
|
||||
public string RegexReplace(string text, string pattern, string replace, string options = null) => RegexFunctions.Replace(Engine, text, pattern, replace, options);
|
||||
|
||||
/// <summary>
|
||||
/// Splits an input string into an array of substrings at the positions defined by a regular expression match.
|
||||
/// </summary>
|
||||
/// <param name="text">The string to split.</param>
|
||||
/// <param name="pattern">The regular expression pattern to match.</param>
|
||||
/// <param name="options">A string with regex options, that can contain the following option characters (default is `null`):
|
||||
/// - `i`: Specifies case-insensitive matching.
|
||||
/// - `m`: Multiline mode. Changes the meaning of `^` and `$` so they match at the beginning and end, respectively, of any line, and not just the beginning and end of the entire string.
|
||||
/// - `s`: Specifies single-line mode. Changes the meaning of the dot `.` so it matches every character (instead of every character except `\n`).
|
||||
/// - `x`: Eliminates unescaped white space from the pattern and enables comments marked with `#`.
|
||||
/// </param>
|
||||
/// <returns>A string array.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "a, b , c, d" |> regex_split `\s*,\s*`
|
||||
/// # "a, b , c, d" |> regex_split(`\s*,\s*`)
|
||||
/// out = ["a", "b", "c", "d"]
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("regex_split", CategoryString)]
|
||||
public ScriptArray RegexSplit(string text, string pattern, string options = null) => RegexFunctions.Split(Engine, text, pattern, options);
|
||||
|
||||
/// <summary>Converts any escaped characters in the input string.</summary>
|
||||
/// <param name="text">The input string containing the text to convert.</param>
|
||||
/// <returns>A string of characters with any escaped characters converted to their unescaped form.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "\\(abc\\.\\*\\)" |> regex_unescape
|
||||
/// # "\\(abc\\.\\*\\)" |> regex_unescape
|
||||
/// out = "(abc.*)"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("regex_unescape", CategoryString)]
|
||||
public string RegexUnescape(string text) => RegexFunctions.Unescape(text);
|
||||
}
|
||||
}
|
||||
16
Kalk/Kalk.Core/Modules/Vectors/IKalkVectorObject.cs
Normal file
16
Kalk/Kalk.Core/Modules/Vectors/IKalkVectorObject.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public interface IKalkVectorObject
|
||||
{
|
||||
int Length { get; }
|
||||
|
||||
Type ElementType { get; }
|
||||
}
|
||||
|
||||
public interface IKalkVectorObject<T> : IKalkVectorObject
|
||||
{
|
||||
object Transform(Func<T, T> apply);
|
||||
}
|
||||
}
|
||||
178
Kalk/Kalk.Core/Modules/Vectors/KalkBool.cs
Normal file
178
Kalk/Kalk.Core/Modules/Vectors/KalkBool.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using Scriban;
|
||||
using Scriban.Parsing;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public struct KalkBool : IEquatable<KalkBool>, IFormattable, IConvertible, IScriptConvertibleTo, IScriptConvertibleFrom, IScriptCustomTypeInfo
|
||||
{
|
||||
private int _value;
|
||||
|
||||
public KalkBool(bool value)
|
||||
{
|
||||
_value = value ? -1 : 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ToString(null, null);
|
||||
}
|
||||
|
||||
public bool TryConvertFrom(TemplateContext context, SourceSpan span, object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
_value = Convert.ToBoolean(value) ? -1 : 0;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryConvertTo(TemplateContext context, SourceSpan span, Type type, out object value)
|
||||
{
|
||||
if (type == typeof(bool))
|
||||
{
|
||||
value = (bool) this;
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
value = Convert.ChangeType(_value, type);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public string ToString(string? format, IFormatProvider? formatProvider)
|
||||
{
|
||||
return _value != 0 ? "true" : "false";
|
||||
}
|
||||
|
||||
public bool Equals(KalkBool other)
|
||||
{
|
||||
return _value == other._value;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is KalkBool other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
public static bool operator ==(KalkBool left, KalkBool right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(KalkBool left, KalkBool right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static implicit operator bool(KalkBool b) => b._value != 0;
|
||||
|
||||
public static implicit operator KalkBool(bool b) => new KalkBool(b);
|
||||
TypeCode IConvertible.GetTypeCode()
|
||||
{
|
||||
return _value.GetTypeCode();
|
||||
}
|
||||
|
||||
bool IConvertible.ToBoolean(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToBoolean(provider);
|
||||
}
|
||||
|
||||
byte IConvertible.ToByte(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToByte(provider);
|
||||
}
|
||||
|
||||
char IConvertible.ToChar(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToChar(provider);
|
||||
}
|
||||
|
||||
DateTime IConvertible.ToDateTime(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToDateTime(provider);
|
||||
}
|
||||
|
||||
decimal IConvertible.ToDecimal(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToDecimal(provider);
|
||||
}
|
||||
|
||||
double IConvertible.ToDouble(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToDouble(provider);
|
||||
}
|
||||
|
||||
short IConvertible.ToInt16(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToInt16(provider);
|
||||
}
|
||||
|
||||
int IConvertible.ToInt32(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToInt32(provider);
|
||||
}
|
||||
|
||||
long IConvertible.ToInt64(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToInt64(provider);
|
||||
}
|
||||
|
||||
sbyte IConvertible.ToSByte(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToSByte(provider);
|
||||
}
|
||||
|
||||
float IConvertible.ToSingle(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToSingle(provider);
|
||||
}
|
||||
|
||||
string IConvertible.ToString(IFormatProvider? provider)
|
||||
{
|
||||
return _value.ToString(provider);
|
||||
}
|
||||
|
||||
object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToType(conversionType, provider);
|
||||
}
|
||||
|
||||
ushort IConvertible.ToUInt16(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToUInt16(provider);
|
||||
}
|
||||
|
||||
uint IConvertible.ToUInt32(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToUInt32(provider);
|
||||
}
|
||||
|
||||
ulong IConvertible.ToUInt64(IFormatProvider? provider)
|
||||
{
|
||||
return ((IConvertible) _value).ToUInt64(provider);
|
||||
}
|
||||
|
||||
public string TypeName => "bool";
|
||||
}
|
||||
}
|
||||
181
Kalk/Kalk.Core/Modules/Vectors/KalkColor.cs
Normal file
181
Kalk/Kalk.Core/Modules/Vectors/KalkColor.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Consolus;
|
||||
using Scriban;
|
||||
using Scriban.Parsing;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
// https://github.com/google/palette.js/tree/master
|
||||
|
||||
[ScriptTypeName("color")]
|
||||
public abstract class KalkColor : KalkVector<byte>
|
||||
{
|
||||
protected KalkColor(int dimension) : base(dimension)
|
||||
{
|
||||
}
|
||||
|
||||
protected KalkColor(IReadOnlyList<byte> list) : base(list)
|
||||
{
|
||||
}
|
||||
|
||||
protected KalkColor(KalkVector<byte> values) : base(values)
|
||||
{
|
||||
}
|
||||
|
||||
public int rgb => (int)((r << 16) | (g << 8) | b);
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
KalkColorRgb.TryGetKnownColor(rgb, out var name);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<string> GetMembers()
|
||||
{
|
||||
for (int i = 0; i < Math.Min(4, Length); i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
yield return "r";
|
||||
break;
|
||||
case 1:
|
||||
yield return "g";
|
||||
break;
|
||||
case 2:
|
||||
yield return "b";
|
||||
break;
|
||||
case 3:
|
||||
yield return "a";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yield return "name";
|
||||
}
|
||||
|
||||
public override bool TryGetValue(TemplateContext context, SourceSpan span, string member, out object result)
|
||||
{
|
||||
if (member == "name")
|
||||
{
|
||||
result = Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.TryGetValue(context, span, member, out result);
|
||||
}
|
||||
|
||||
|
||||
private static float Clamp01(float value) => Math.Clamp(value, 0.0f, 1.0f);
|
||||
|
||||
public KalkVector<float> GetFloatVector(int targetDimension)
|
||||
{
|
||||
return this is KalkColorRgb
|
||||
? targetDimension == 4 ?
|
||||
new KalkVector<float>(Clamp01(this[0] / 255.0f), Clamp01(this[1] / 255.0f), Clamp01(this[2] / 255.0f), 1.0f):
|
||||
new KalkVector<float>(Clamp01(this[0] / 255.0f), Clamp01(this[1] / 255.0f), Clamp01(this[2] / 255.0f))
|
||||
: new KalkVector<float>(Clamp01(this[0] / 255.0f), Clamp01(this[1] / 255.0f), Clamp01(this[2] / 255.0f), Clamp01(this[3] / 255.0f));
|
||||
}
|
||||
|
||||
protected override object GetSwizzleValue(ComponentKind kind, byte result)
|
||||
{
|
||||
return kind == ComponentKind.xyzw ? (object)Clamp01(result / 255.0f) : result;
|
||||
}
|
||||
|
||||
protected override byte TransformComponentToSet(TemplateContext context, SourceSpan span, ComponentKind kind, object value)
|
||||
{
|
||||
return kind == ComponentKind.xyzw ? (byte)(Clamp01(context.ToObject<float>(span, value)) * 255) : base.TransformComponentToSet(context, span, kind, value);
|
||||
}
|
||||
|
||||
protected override KalkVector NewVector(ComponentKind kind, IReadOnlyList<byte> list)
|
||||
{
|
||||
if (kind == ComponentKind.xyzw)
|
||||
{
|
||||
if (list.Count == 4)
|
||||
{
|
||||
return new KalkVector<float>(Clamp01(list[0] / 255.0f), Clamp01(list[1] / 255.0f), Clamp01(list[2] / 255.0f), Clamp01(list[3] / 255.0f));
|
||||
}
|
||||
|
||||
if (list.Count == 3)
|
||||
{
|
||||
return new KalkVector<float>(Clamp01(list[0] / 255.0f), Clamp01(list[1] / 255.0f), Clamp01(list[2] / 255.0f));
|
||||
}
|
||||
|
||||
if (list.Count == 2)
|
||||
{
|
||||
return new KalkVector<float>(Clamp01(list[0] / 255.0f), Clamp01(list[1] / 255.0f));
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Cannot create a float vector type from this rgba components");
|
||||
}
|
||||
else
|
||||
{
|
||||
return list.Count == 4 ? new KalkColorRgba(list[0], list[1], list[2], list[3]) : list.Count == 3 ? new KalkColorRgb(list[0], list[1], list[2]) : base.NewVector(kind, list);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract override KalkVector<byte> NewVector(int length);
|
||||
|
||||
public abstract override string TypeName { get; }
|
||||
|
||||
public override string ToString(string format, IFormatProvider formatProvider)
|
||||
{
|
||||
var engine = formatProvider as KalkEngine;
|
||||
var builder = new StringBuilder();
|
||||
builder.Append(TypeName);
|
||||
builder.Append('(');
|
||||
var length = this is KalkColorRgb ? 3 : 4;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (i > 0) builder.Append(", ");
|
||||
builder.Append(engine != null ? engine.ObjectToString(this[i]) : this[i].ToString(null, formatProvider));
|
||||
}
|
||||
builder.Append(')');
|
||||
|
||||
bool isAligned = format == "aligned";
|
||||
|
||||
// rgb(240, 248, 255)
|
||||
// rgb(255, 255, 255, 255)
|
||||
if (isAligned)
|
||||
{
|
||||
if (length == 3)
|
||||
{
|
||||
builder.Append(' ', "rgb(255, 255, 255)".Length - builder.Length);
|
||||
}
|
||||
else if (length == 4)
|
||||
{
|
||||
builder.Append(' ', "rgb(255, 255, 255, 255)".Length - builder.Length);
|
||||
}
|
||||
}
|
||||
|
||||
builder.Append(" ## ");
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
builder.Append($"{this[i]:X2}");
|
||||
}
|
||||
|
||||
if (engine != null && engine.IsOutputSupportHighlighting)
|
||||
{
|
||||
builder.Append(" ");
|
||||
builder.Append(ConsoleStyle.BackgroundRgb(this[0], this[1], this[2]));
|
||||
builder.Append(" ");
|
||||
builder.Append(ConsoleStyle.Reset);
|
||||
}
|
||||
|
||||
// Add known color name
|
||||
if (KalkColorRgb.TryGetKnownColor(rgb, out var colorName))
|
||||
{
|
||||
builder.Append(isAligned ? $" {colorName,-20}" : $" {colorName}");
|
||||
}
|
||||
|
||||
builder.Append(" ##");
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
69
Kalk/Kalk.Core/Modules/Vectors/KalkColorConstructor.cs
Normal file
69
Kalk/Kalk.Core/Modules/Vectors/KalkColorConstructor.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Scriban;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public abstract class KalkColorConstructor : KalkVectorConstructor<byte>
|
||||
{
|
||||
protected KalkColorConstructor(int dimension) : base(dimension)
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract override KalkVector<byte> NewVector(int dimension);
|
||||
|
||||
protected override byte GetArgumentValue(TemplateContext context, object arg)
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
case float f32:
|
||||
return (byte)(255 * Math.Clamp(f32, 0.0f, 1.0f));
|
||||
case double f64:
|
||||
return (byte)(255 * Math.Clamp(f64, 0.0, 1.0));
|
||||
case decimal dec:
|
||||
return (byte)(255 * Math.Clamp(dec, 0.0m, 1.0m));
|
||||
default:
|
||||
return base.GetArgumentValue(context, arg);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ProcessSingleArgument(TemplateContext context, ref int index, object arg, KalkVector<byte> vector)
|
||||
{
|
||||
int value;
|
||||
switch (arg)
|
||||
{
|
||||
case string rgbStr:
|
||||
try
|
||||
{
|
||||
if (!KalkColorRgb.TryGetKnownColor(rgbStr, out value))
|
||||
{
|
||||
value = int.Parse(rgbStr.TrimStart('#'), System.Globalization.NumberStyles.HexNumber);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new ScriptArgumentException(0, $"Expecting a known color (e.g `AliceBlue`) or an hexadecimal rgb string (e.g #FF80C2) instead of `{rgbStr}`. Type `colors` for listing known colors.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (arg is IList)
|
||||
{
|
||||
base.ProcessSingleArgument(context, ref index, arg, vector);
|
||||
return;
|
||||
}
|
||||
value = context.ToObject<int>(context.CurrentSpan, arg);
|
||||
break;
|
||||
}
|
||||
|
||||
vector[index++] = (byte)((value >> 16) & 0xFF);
|
||||
vector[index++] = (byte)((value >> 8) & 0xFF);
|
||||
vector[index++] = (byte)(value & 0xFF);
|
||||
|
||||
if (Dimension == 4)
|
||||
{
|
||||
vector[index++] = (byte)((value >> 24) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
220
Kalk/Kalk.Core/Modules/Vectors/KalkColorRgb.cs
Normal file
220
Kalk/Kalk.Core/Modules/Vectors/KalkColorRgb.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
[ScriptTypeName("rgb")]
|
||||
public class KalkColorRgb : KalkColor
|
||||
{
|
||||
public KalkColorRgb() : base(4)
|
||||
{
|
||||
this[3] = 0xFF;
|
||||
}
|
||||
|
||||
public KalkColorRgb(KalkColorRgb values) : base(values)
|
||||
{
|
||||
this[3] = 0xFF;
|
||||
}
|
||||
|
||||
public KalkColorRgb(byte r, byte g, byte b) : this()
|
||||
{
|
||||
this[0] = r;
|
||||
this[1] = g;
|
||||
this[2] = b;
|
||||
}
|
||||
|
||||
public KalkColorRgb(int rgb) : this()
|
||||
{
|
||||
this[0] = (byte)((rgb >> 16) & 0xFF);
|
||||
this[1] = (byte)((rgb >> 8) & 0xFF);
|
||||
this[2] = (byte)(rgb & 0xFF);
|
||||
this[3] = 0xFF;
|
||||
}
|
||||
|
||||
public override string TypeName => "rgb";
|
||||
|
||||
public override KalkVector Clone()
|
||||
{
|
||||
return new KalkColorRgb(this);
|
||||
}
|
||||
|
||||
protected override KalkVector<byte> NewVector(int length) => new KalkColorRgb();
|
||||
|
||||
|
||||
public static bool TryGetKnownColor(string color, out int rgb)
|
||||
{
|
||||
if (color == null) throw new ArgumentNullException(nameof(color));
|
||||
return KnownColors.TryGetValue(color, out rgb);
|
||||
}
|
||||
|
||||
public static bool TryGetKnownColor(int rgb, out string color)
|
||||
{
|
||||
return ReverseKnownColors.TryGetValue(rgb, out color);
|
||||
}
|
||||
|
||||
public static List<KalkColorRgb> GetKnownColors()
|
||||
{
|
||||
return KnownColors.OrderBy(x => x.Key).Select(x => new KalkColorRgb(x.Value)).ToList();
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, int> KnownColors = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"AliceBlue", 0xF0F8FF},
|
||||
{"AntiqueWhite", 0xFAEBD7},
|
||||
{"Aqua", 0x00FFFF},
|
||||
{"Aquamarine", 0x7FFFD4},
|
||||
{"Azure", 0xF0FFFF},
|
||||
{"Beige", 0xF5F5DC},
|
||||
{"Bisque", 0xFFE4C4},
|
||||
{"Black", 0x0},
|
||||
{"BlanchedAlmond", 0xFFEBCD},
|
||||
{"Blue", 0x0000FF},
|
||||
{"BlueViolet", 0x8A2BE2},
|
||||
{"Brown", 0xA52A2A},
|
||||
{"BurlyWood", 0xDEB887},
|
||||
{"CadetBlue", 0x5F9EA0},
|
||||
{"Chartreuse", 0x7FFF00},
|
||||
{"Chocolate", 0xD2691E},
|
||||
{"Coral", 0xFF7F50},
|
||||
{"CornflowerBlue", 0x6495ED},
|
||||
{"Cornsilk", 0xFFF8DC},
|
||||
{"Crimson", 0xDC143C},
|
||||
{"Cyan", 0x00FFFF},
|
||||
{"DarkBlue", 0x00008B},
|
||||
{"DarkCyan", 0x008B8B},
|
||||
{"DarkGoldenrod", 0xB8860B},
|
||||
{"DarkGray", 0xA9A9A9},
|
||||
{"DarkGreen", 0x6400},
|
||||
{"DarkKhaki", 0xBDB76B},
|
||||
{"DarkMagenta", 0x8B008B},
|
||||
{"DarkOliveGreen", 0x556B2F},
|
||||
{"DarkOrange", 0xFF8C00},
|
||||
{"DarkOrchid", 0x9932CC},
|
||||
{"DarkRed", 0x8B0000},
|
||||
{"DarkSalmon", 0xE9967A},
|
||||
{"DarkSeaGreen", 0x8FBC8F},
|
||||
{"DarkSlateBlue", 0x483D8B},
|
||||
{"DarkSlateGray", 0x2F4F4F},
|
||||
{"DarkTurquoise", 0x00CED1},
|
||||
{"DarkViolet", 0x9400D3},
|
||||
{"DeepPink", 0xFF1493},
|
||||
{"DeepSkyBlue", 0x00BFFF},
|
||||
{"DimGray", 0x696969},
|
||||
{"DodgerBlue", 0x1E90FF},
|
||||
{"Firebrick", 0xB22222},
|
||||
{"FloralWhite", 0xFFFAF0},
|
||||
{"ForestGreen", 0x228B22},
|
||||
{"Fuchsia", 0xFF00FF},
|
||||
{"Gainsboro", 0xDCDCDC},
|
||||
{"GhostWhite", 0xF8F8FF},
|
||||
{"Gold", 0xFFD700},
|
||||
{"Goldenrod", 0xDAA520},
|
||||
{"Gray", 0x808080},
|
||||
{"Green", 0x8000},
|
||||
{"GreenYellow", 0xADFF2F},
|
||||
{"Honeydew", 0xF0FFF0},
|
||||
{"HotPink", 0xFF69B4},
|
||||
{"IndianRed", 0xCD5C5C},
|
||||
{"Indigo", 0x4B0082},
|
||||
{"Ivory", 0xFFFFF0},
|
||||
{"Khaki", 0xF0E68C},
|
||||
{"Lavender", 0xE6E6FA},
|
||||
{"LavenderBlush", 0xFFF0F5},
|
||||
{"LawnGreen", 0x7CFC00},
|
||||
{"LemonChiffon", 0xFFFACD},
|
||||
{"LightBlue", 0xADD8E6},
|
||||
{"LightCoral", 0xF08080},
|
||||
{"LightCyan", 0xE0FFFF},
|
||||
{"LightGoldenrodYellow", 0xFAFAD2},
|
||||
{"LightGreen", 0x90EE90},
|
||||
{"LightGray", 0xD3D3D3},
|
||||
{"LightPink", 0xFFB6C1},
|
||||
{"LightSalmon", 0xFFA07A},
|
||||
{"LightSeaGreen", 0x20B2AA},
|
||||
{"LightSkyBlue", 0x87CEFA},
|
||||
{"LightSlateGray", 0x778899},
|
||||
{"LightSteelBlue", 0xB0C4DE},
|
||||
{"LightYellow", 0xFFFFE0},
|
||||
{"Lime", 0x00FF00},
|
||||
{"LimeGreen", 0x32CD32},
|
||||
{"Linen", 0xFAF0E6},
|
||||
{"Magenta", 0xFF00FF},
|
||||
{"Maroon", 0x800000},
|
||||
{"MediumAquamarine", 0x66CDAA},
|
||||
{"MediumBlue", 0x0000CD},
|
||||
{"MediumOrchid", 0xBA55D3},
|
||||
{"MediumPurple", 0x9370DB},
|
||||
{"MediumSeaGreen", 0x3CB371},
|
||||
{"MediumSlateBlue", 0x7B68EE},
|
||||
{"MediumSpringGreen", 0x00FA9A},
|
||||
{"MediumTurquoise", 0x48D1CC},
|
||||
{"MediumVioletRed", 0xC71585},
|
||||
{"MidnightBlue", 0x191970},
|
||||
{"MintCream", 0xF5FFFA},
|
||||
{"MistyRose", 0xFFE4E1},
|
||||
{"Moccasin", 0xFFE4B5},
|
||||
{"NavajoWhite", 0xFFDEAD},
|
||||
{"Navy", 0x80},
|
||||
{"OldLace", 0xFDF5E6},
|
||||
{"Olive", 0x808000},
|
||||
{"OliveDrab", 0x6B8E23},
|
||||
{"Orange", 0xFFA500},
|
||||
{"OrangeRed", 0xFF4500},
|
||||
{"Orchid", 0xDA70D6},
|
||||
{"PaleGoldenrod", 0xEEE8AA},
|
||||
{"PaleGreen", 0x98FB98},
|
||||
{"PaleTurquoise", 0xAFEEEE},
|
||||
{"PaleVioletRed", 0xDB7093},
|
||||
{"PapayaWhip", 0xFFEFD5},
|
||||
{"PeachPuff", 0xFFDAB9},
|
||||
{"Peru", 0xCD853F},
|
||||
{"Pink", 0xFFC0CB},
|
||||
{"Plum", 0xDDA0DD},
|
||||
{"PowderBlue", 0xB0E0E6},
|
||||
{"Purple", 0x800080},
|
||||
{"RebeccaPurple", 0x663399},
|
||||
{"Red", 0xFF0000},
|
||||
{"RosyBrown", 0xBC8F8F},
|
||||
{"RoyalBlue", 0x41690},
|
||||
{"SaddleBrown", 0x8B4513},
|
||||
{"Salmon", 0xFA8072},
|
||||
{"SandyBrown", 0xF4A460},
|
||||
{"SeaGreen", 0x2E8B57},
|
||||
{"SeaShell", 0xFFF5EE},
|
||||
{"Sienna", 0xA0522D},
|
||||
{"Silver", 0xC0C0C0},
|
||||
{"SkyBlue", 0x87CEEB},
|
||||
{"SlateBlue", 0x6A5ACD},
|
||||
{"SlateGray", 0x708090},
|
||||
{"Snow", 0xFFFAFA},
|
||||
{"SpringGreen", 0x00FF7F},
|
||||
{"SteelBlue", 0x4682B4},
|
||||
{"Tan", 0xD2B48C},
|
||||
{"Teal", 0x8080},
|
||||
{"Thistle", 0xD8BFD8},
|
||||
{"Tomato", 0xFF6347},
|
||||
{"Turquoise", 0x40E0D0},
|
||||
{"Violet", 0xEE82EE},
|
||||
{"Wheat", 0xF5DEB3},
|
||||
{"White", 0xFFFFFF},
|
||||
{"WhiteSmoke", 0xF5F5F5},
|
||||
{"Yellow", 0xFFFF00},
|
||||
{"YellowGreen", 0x9ACD32},
|
||||
};
|
||||
|
||||
private static readonly Dictionary<int, string> ReverseKnownColors = new Dictionary<int, string>();
|
||||
|
||||
static KalkColorRgb()
|
||||
{
|
||||
foreach (var knownColor in KnownColors)
|
||||
{
|
||||
if (!ReverseKnownColors.ContainsKey(knownColor.Value))
|
||||
{
|
||||
ReverseKnownColors.Add(knownColor.Value, knownColor.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Kalk/Kalk.Core/Modules/Vectors/KalkColorRgbConstructor.cs
Normal file
11
Kalk/Kalk.Core/Modules/Vectors/KalkColorRgbConstructor.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public class KalkColorRgbConstructor : KalkColorConstructor
|
||||
{
|
||||
public KalkColorRgbConstructor() : base(3)
|
||||
{
|
||||
}
|
||||
|
||||
protected override KalkVector<byte> NewVector(int dimension) => new KalkColorRgb();
|
||||
}
|
||||
}
|
||||
42
Kalk/Kalk.Core/Modules/Vectors/KalkColorRgba.cs
Normal file
42
Kalk/Kalk.Core/Modules/Vectors/KalkColorRgba.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
[ScriptTypeName("rgba")]
|
||||
public class KalkColorRgba : KalkColor
|
||||
{
|
||||
public KalkColorRgba() : base(4)
|
||||
{
|
||||
}
|
||||
|
||||
public KalkColorRgba(byte r, byte g, byte b, byte a) : this()
|
||||
{
|
||||
this[0] = r;
|
||||
this[1] = g;
|
||||
this[2] = b;
|
||||
this[3] = a;
|
||||
}
|
||||
|
||||
public KalkColorRgba(int rgb) : this()
|
||||
{
|
||||
this[0] = (byte)((rgb >> 16) & 0xFF);
|
||||
this[1] = (byte)((rgb >> 8) & 0xFF);
|
||||
this[2] = (byte)(rgb & 0xFF);
|
||||
this[3] = (byte)((rgb >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
public KalkColorRgba(KalkColorRgba values) : base(values)
|
||||
{
|
||||
}
|
||||
|
||||
public override string TypeName => "rgba";
|
||||
|
||||
public override KalkVector Clone()
|
||||
{
|
||||
return new KalkColorRgba(this);
|
||||
}
|
||||
|
||||
|
||||
protected override KalkVector<byte> NewVector(int length) => new KalkColorRgba();
|
||||
}
|
||||
}
|
||||
11
Kalk/Kalk.Core/Modules/Vectors/KalkColorRgbaConstructor.cs
Normal file
11
Kalk/Kalk.Core/Modules/Vectors/KalkColorRgbaConstructor.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public class KalkColorRgbaConstructor : KalkColorConstructor
|
||||
{
|
||||
public KalkColorRgbaConstructor() : base(4)
|
||||
{
|
||||
}
|
||||
|
||||
protected override KalkVector<byte> NewVector(int dimension) => new KalkColorRgba();
|
||||
}
|
||||
}
|
||||
1201
Kalk/Kalk.Core/Modules/Vectors/KalkMatrix.cs
Normal file
1201
Kalk/Kalk.Core/Modules/Vectors/KalkMatrix.cs
Normal file
File diff suppressed because it is too large
Load Diff
1302
Kalk/Kalk.Core/Modules/Vectors/KalkVector.cs
Normal file
1302
Kalk/Kalk.Core/Modules/Vectors/KalkVector.cs
Normal file
File diff suppressed because it is too large
Load Diff
159
Kalk/Kalk.Core/Modules/Vectors/KalkVectorConstructor.cs
Normal file
159
Kalk/Kalk.Core/Modules/Vectors/KalkVectorConstructor.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Kalk.Core.Helpers;
|
||||
using Scriban;
|
||||
using Scriban.Helpers;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public class KalkVectorConstructor<T> : KalkConstructor where T : unmanaged
|
||||
{
|
||||
public KalkVectorConstructor(int dimension)
|
||||
{
|
||||
Dimension = dimension;
|
||||
}
|
||||
|
||||
public int Dimension { get; }
|
||||
|
||||
public KalkVector<T> Invoke(TemplateContext context, object[] arguments)
|
||||
{
|
||||
if (arguments.Length == 0)
|
||||
{
|
||||
return NewVector(Dimension);
|
||||
}
|
||||
|
||||
var vector = NewVector(Dimension);
|
||||
int index = 0;
|
||||
if (arguments.Length == 1)
|
||||
{
|
||||
var arg = arguments[0];
|
||||
// Replace implicitly Rgb/Rgba to xyzw
|
||||
if (arg is KalkColor color)
|
||||
{
|
||||
if (this is KalkColorConstructor)
|
||||
{
|
||||
var colorLength = color is KalkColorRgb ? 3 : 4;
|
||||
if (Dimension != colorLength)
|
||||
{
|
||||
if (Dimension == 3) // 4 to 3
|
||||
{
|
||||
arg = new KalkVector<byte>(color.r, color.g, color.b);
|
||||
}
|
||||
else // 3 to 4
|
||||
{
|
||||
Debug.Assert(Dimension == 4);
|
||||
arg = new KalkVector<byte>(color.r, color.g, color.b, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = color.GetFloatVector(Dimension);
|
||||
}
|
||||
}
|
||||
var argLength = GetArgLength(arg, true);
|
||||
var length = index + argLength;
|
||||
if (length != Dimension)
|
||||
{
|
||||
throw new ScriptArgumentException(0, $"Invalid number of arguments for {vector.TypeName}. Expecting {Dimension} arguments instead of {length}.");
|
||||
}
|
||||
|
||||
ProcessSingleArgument(context, ref index, arg, vector);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < arguments.Length; i++)
|
||||
{
|
||||
var arg = arguments[i];
|
||||
var argLength = GetArgLength(arg, false);
|
||||
|
||||
var length = index + argLength;
|
||||
if (length > Dimension)
|
||||
{
|
||||
throw new ScriptArgumentException(i, $"Invalid number of arguments for {vector.TypeName}. Expecting {Dimension} arguments instead of {length}.");
|
||||
}
|
||||
|
||||
ProcessArgument(context, ref index, arg, vector);
|
||||
}
|
||||
}
|
||||
|
||||
if (index != Dimension)
|
||||
{
|
||||
throw new ScriptArgumentException(arguments.Length - 1, $"Invalid number of arguments for {vector.TypeName}. Expecting {Dimension} arguments instead of {index}.");
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
private int GetArgLength(object arg, bool isSingleArg)
|
||||
{
|
||||
if (arg is IList list)
|
||||
{
|
||||
int argLength = 0;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
argLength += GetArgLength(list[i], false);
|
||||
}
|
||||
return argLength;
|
||||
}
|
||||
|
||||
return isSingleArg ? Dimension : 1;
|
||||
}
|
||||
|
||||
private void AddListItem(TemplateContext context, ref int index, object arg, KalkVector<T> vector)
|
||||
{
|
||||
if (arg is IList list)
|
||||
{
|
||||
var count = list.Count;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
AddListItem(context, ref index, list[j], vector);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = GetArgumentValue(context, arg);
|
||||
vector[index++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ProcessSingleArgument(TemplateContext context, ref int index, object arg, KalkVector<T> vector)
|
||||
{
|
||||
if (arg is IList list)
|
||||
{
|
||||
AddListItem(context, ref index, list, vector);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = GetArgumentValue(context, arg);
|
||||
for (int j = 0; j < Dimension; j++)
|
||||
{
|
||||
vector[index++] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void ProcessArgument(TemplateContext context, ref int index, object arg, KalkVector<T> vector)
|
||||
{
|
||||
if (arg is IList list)
|
||||
{
|
||||
AddListItem(context, ref index, list, vector);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = GetArgumentValue(context, arg);
|
||||
vector[index++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual KalkVector<T> NewVector(int dimension) => new KalkVector<T>(dimension);
|
||||
|
||||
protected virtual T GetArgumentValue(TemplateContext context, object value)
|
||||
{
|
||||
return context.ToObject<T>(context.CurrentSpan, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Kalk/Kalk.Core/Modules/Vectors/PrimitiveSwizzleAccessor.cs
Normal file
111
Kalk/Kalk.Core/Modules/Vectors/PrimitiveSwizzleAccessor.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Scriban;
|
||||
using Scriban.Parsing;
|
||||
using Scriban.Runtime;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core
|
||||
{
|
||||
public class PrimitiveSwizzleAccessor : IObjectAccessor
|
||||
{
|
||||
public static readonly PrimitiveSwizzleAccessor Default = new PrimitiveSwizzleAccessor();
|
||||
|
||||
private PrimitiveSwizzleAccessor()
|
||||
{
|
||||
}
|
||||
|
||||
public int GetMemberCount(TemplateContext context, SourceSpan span, object target)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetMembers(TemplateContext context, SourceSpan span, object target)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
public bool HasMember(TemplateContext context, SourceSpan span, object target, string member)
|
||||
{
|
||||
return IsSwizzle(member);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TemplateContext context, SourceSpan span, object target, string member, out object value)
|
||||
{
|
||||
var targetFloat = context.ToObject<float>(span, target);
|
||||
|
||||
if (member.Length == 1)
|
||||
{
|
||||
value = targetFloat;
|
||||
return true;
|
||||
}
|
||||
|
||||
var vector = new KalkVector<float>(member.Length);
|
||||
int index = 0;
|
||||
for(int i = 0; i < member.Length; i++)
|
||||
{
|
||||
var c = member[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'x':
|
||||
vector[index] = targetFloat;
|
||||
break;
|
||||
case 'y':
|
||||
vector[index] = targetFloat;
|
||||
break;
|
||||
case 'z':
|
||||
vector[index] = targetFloat;
|
||||
break;
|
||||
case 'w':
|
||||
vector[index] = targetFloat;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
value = vector;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TrySetValue(TemplateContext context, SourceSpan span, object target, string member, object value)
|
||||
{
|
||||
throw new ScriptRuntimeException(span, "Cannot set a member on a primitive");
|
||||
}
|
||||
|
||||
public bool TryGetItem(TemplateContext context, SourceSpan span, object target, object index, out object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool TrySetItem(TemplateContext context, SourceSpan span, object target, object index, object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool HasIndexer => false;
|
||||
|
||||
public Type IndexType => typeof(int);
|
||||
|
||||
private static bool IsSwizzle(string text)
|
||||
{
|
||||
if (text.Length > 4) return false;
|
||||
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
var c = text[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'x':
|
||||
case 'y':
|
||||
case 'z':
|
||||
case 'w':
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
538
Kalk/Kalk.Core/Modules/Vectors/TypesModule.cs
Normal file
538
Kalk/Kalk.Core/Modules/Vectors/TypesModule.cs
Normal file
@@ -0,0 +1,538 @@
|
||||
using System;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
public sealed partial class TypesModule : KalkModuleWithFunctions
|
||||
{
|
||||
public const string CategoryTypeConstructors = "Type Constructors";
|
||||
public const string CategoryVectorTypeConstructors = "Type Vector Constructors";
|
||||
private static readonly KalkColorRgbConstructor RgbConstructor = new KalkColorRgbConstructor();
|
||||
private static readonly KalkColorRgbaConstructor RgbaConstructor = new KalkColorRgbaConstructor();
|
||||
|
||||
public TypesModule() : base("Types")
|
||||
{
|
||||
IsBuiltin = true;
|
||||
RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an unsigned byte value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>An unsigned byte value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> byte
|
||||
/// # byte
|
||||
/// out = 0
|
||||
/// >>> byte 0
|
||||
/// # byte(0)
|
||||
/// out = 0
|
||||
/// >>> byte 255
|
||||
/// # byte(255)
|
||||
/// out = 255
|
||||
/// ```
|
||||
/// </example>
|
||||
/// <test>
|
||||
/// ```kalk
|
||||
/// >>> byte 256
|
||||
/// Unable to convert type `int` to `byte`
|
||||
/// ```
|
||||
/// </test>
|
||||
[KalkExport("byte", CategoryTypeConstructors)]
|
||||
public byte CreateByte(object value = null) => value == null ? (byte)0 : Engine.ToObject<byte>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signed-byte value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>A signed-byte value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> sbyte
|
||||
/// # sbyte
|
||||
/// out = 0
|
||||
/// >>> sbyte 0
|
||||
/// # sbyte(0)
|
||||
/// out = 0
|
||||
/// >>> sbyte 127
|
||||
/// # sbyte(127)
|
||||
/// out = 127
|
||||
/// >>> sbyte(-128)
|
||||
/// # sbyte(-128)
|
||||
/// out = -128
|
||||
/// ```
|
||||
/// </example>
|
||||
/// <test>
|
||||
/// ```kalk
|
||||
/// >>> sbyte 128
|
||||
/// Unable to convert type `int` to `sbyte`
|
||||
/// ```
|
||||
/// </test>
|
||||
[KalkExport("sbyte", CategoryTypeConstructors)]
|
||||
public sbyte CreateSByte(object value = null) => value == null ? (sbyte)0 : Engine.ToObject<sbyte>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signed-short (16-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>A signed-short (16-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> short
|
||||
/// # short
|
||||
/// out = 0
|
||||
/// >>> short 0
|
||||
/// # short(0)
|
||||
/// out = 0
|
||||
/// >>> short 32767
|
||||
/// # short(32767)
|
||||
/// out = 32_767
|
||||
/// >>> short(-32768)
|
||||
/// # short(-32768)
|
||||
/// out = -32_768
|
||||
/// >>> short 32768
|
||||
/// Unable to convert type `int` to `short`
|
||||
/// ```
|
||||
/// </example>
|
||||
/// <test>
|
||||
/// ```kalk
|
||||
/// >>> short 32768
|
||||
/// Unable to convert type `int` to `short`
|
||||
/// ```
|
||||
/// </test>
|
||||
[KalkExport("short", CategoryTypeConstructors)]
|
||||
public short CreateShort(object value = null) => value == null ? (short)0 : Engine.ToObject<short>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an unsigned short (16-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>An unsigned short (16-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> ushort
|
||||
/// # ushort
|
||||
/// out = 0
|
||||
/// >>> ushort 0
|
||||
/// # ushort(0)
|
||||
/// out = 0
|
||||
/// >>> ushort 65535
|
||||
/// # ushort(65535)
|
||||
/// out = 65_535
|
||||
/// ```
|
||||
/// </example>
|
||||
/// <test>
|
||||
/// ```kalk
|
||||
/// >>> ushort 65536
|
||||
/// Unable to convert type `int` to `ushort`
|
||||
/// ```
|
||||
/// </test>
|
||||
[KalkExport("ushort", CategoryTypeConstructors)]
|
||||
public ushort CreateUShort(object value = null) => value == null ? (ushort)0 : Engine.ToObject<ushort>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an unsigned int (32-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>An unsigned int (32-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> uint
|
||||
/// # uint
|
||||
/// out = 0
|
||||
/// >>> uint 0
|
||||
/// # uint(0)
|
||||
/// out = 0
|
||||
/// >>> uint(1<<32 - 1)
|
||||
/// # uint(1 << 32 - 1)
|
||||
/// out = 4_294_967_295
|
||||
/// ```
|
||||
/// </example>
|
||||
/// <test>
|
||||
/// ```kalk
|
||||
/// >>> uint 1 << 32
|
||||
/// Unable to convert type `long` to `uint`
|
||||
/// ```
|
||||
/// </test>
|
||||
[KalkExport("uint", CategoryTypeConstructors)]
|
||||
public uint CreateUInt(object value = null) => value == null ? 0U : Engine.ToObject<uint>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signed-int (32-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>A signed-int (32-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> int
|
||||
/// # int
|
||||
/// out = 0
|
||||
/// >>> int 0
|
||||
/// # int(0)
|
||||
/// out = 0
|
||||
/// >>> int(1 << 31 - 1)
|
||||
/// # int(1 << 31 - 1)
|
||||
/// out = 2_147_483_647
|
||||
/// >>> int(-(1<<31))
|
||||
/// # int(-(1 << 31))
|
||||
/// out = -2_147_483_648
|
||||
/// ```
|
||||
/// </example>
|
||||
/// <test>
|
||||
/// ```kalk
|
||||
/// >>> int 1 << 31
|
||||
/// Unable to convert type `long` to int
|
||||
/// ```
|
||||
/// </test>
|
||||
[KalkExport("int", CategoryTypeConstructors)]
|
||||
public int CreateInt(object value = null) => value == null ? 0 : Engine.ToObject<int>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an unsigned long (64-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>An unsigned long (64-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> ulong
|
||||
/// # ulong
|
||||
/// out = 0
|
||||
/// >>> ulong 0
|
||||
/// # ulong(0)
|
||||
/// out = 0
|
||||
/// >>> ulong(1 << 64 - 1)
|
||||
/// # ulong(1 << 64 - 1)
|
||||
/// out = 18_446_744_073_709_551_615
|
||||
/// ```
|
||||
/// </example>
|
||||
/// <test>
|
||||
/// ```kalk
|
||||
/// >>> ulong 1 << 64
|
||||
/// Unable to convert type `bigint` to `ulong`
|
||||
/// ```
|
||||
/// </test>
|
||||
[KalkExport("ulong", CategoryTypeConstructors)]
|
||||
public ulong CreateULong(object value = null) => value == null ? 0UL : Engine.ToObject<ulong>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signed-long (64-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>A signed-long (64-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> long
|
||||
/// # long
|
||||
/// out = 0
|
||||
/// >>> long 0
|
||||
/// # long(0)
|
||||
/// out = 0
|
||||
/// >>> long(1 << 63 - 1)
|
||||
/// # long(1 << 63 - 1)
|
||||
/// out = 9_223_372_036_854_775_807
|
||||
/// >>> long(-(1<<63))
|
||||
/// # long(-(1 << 63))
|
||||
/// out = -9_223_372_036_854_775_808
|
||||
/// ```
|
||||
/// </example>
|
||||
/// <test>
|
||||
/// ```kalk
|
||||
/// >>> long 1 << 63
|
||||
/// Unable to convert type `bigint` to `long`
|
||||
/// ```
|
||||
/// </test>
|
||||
[KalkExport("long", CategoryTypeConstructors)]
|
||||
public long CreateLong(object value = null) => value == null ? 0L : Engine.ToObject<long>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a boolean value (32-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>A boolean (32-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> bool 1
|
||||
/// # bool(1)
|
||||
/// out = true
|
||||
/// >>> bool 0
|
||||
/// # bool(0)
|
||||
/// out = false
|
||||
/// >>> bool true
|
||||
/// # bool(true)
|
||||
/// out = true
|
||||
/// >>> bool false
|
||||
/// # bool(false)
|
||||
/// out = false
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("bool", CategoryTypeConstructors)]
|
||||
public KalkBool CreateBool(object value = null) => value != null && Engine.ToObject<KalkBool>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a float value (32-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>A float (32-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> float(1)
|
||||
/// # float(1)
|
||||
/// out = 1
|
||||
/// >>> float(-1)
|
||||
/// # float(-1)
|
||||
/// out = -1
|
||||
/// >>> float(100000000000)
|
||||
/// # float(100000000000)
|
||||
/// out = 1E+11
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("float", CategoryTypeConstructors)]
|
||||
public float CreateFloat(object value = null) => value == null ? 0.0f : Engine.ToObject<float>(0, value);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a half float value (16-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>A half float (16-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> half(1)
|
||||
/// # half(1)
|
||||
/// out = 1
|
||||
/// >>> half(-1)
|
||||
/// # half(-1)
|
||||
/// out = -1
|
||||
/// >>> half(1000.5)
|
||||
/// # half(1000.5)
|
||||
/// out = 1000.5
|
||||
/// >>> kind out
|
||||
/// # kind(out)
|
||||
/// out = "half"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("half", CategoryTypeConstructors)]
|
||||
public KalkHalf CreateHalf(object value = null) => value == null ? (KalkHalf)0.0f : Engine.ToObject<KalkHalf>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a double value (64-bit) value.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value.</param>
|
||||
/// <returns>A double (64-bit) value</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> double(1)
|
||||
/// # double(1)
|
||||
/// out = 1
|
||||
/// >>> double(-1)
|
||||
/// # double(-1)
|
||||
/// out = -1
|
||||
/// >>> double(100000000000)
|
||||
/// # double(100000000000)
|
||||
/// out = 100000000000
|
||||
/// >>> double(1<<200)
|
||||
/// # double(1 << 200)
|
||||
/// out = 1.6069380442589903E+60
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("double", CategoryTypeConstructors)]
|
||||
public double CreateDouble(object value = null) => value == null ? 0.0 : Engine.ToObject<double>(0, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a vector of the specified element type, with the number of elements and optional values.
|
||||
/// </summary>
|
||||
/// <param name="name">The element type of the vector (e.g float).</param>
|
||||
/// <param name="dimension">The dimension of the vector.</param>
|
||||
/// <param name="arguments">The optional values (must have 1 or dimension elements).</param>
|
||||
/// <returns>A matrix of the specified row x column.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> vector(float, 4, 5..8)
|
||||
/// # vector(float, 4, 5..8)
|
||||
/// out = float4(5, 6, 7, 8)
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("vector", CategoryVectorTypeConstructors)]
|
||||
public object CreateVector(ScriptVariable name, int dimension, params object[] arguments)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (dimension <= 1) throw new ArgumentOutOfRangeException(nameof(dimension), "Invalid dimension. Expecting a value > 1.");
|
||||
switch (name.Name)
|
||||
{
|
||||
case "int":
|
||||
switch (dimension)
|
||||
{
|
||||
case 2: return CreateInt2(arguments);
|
||||
case 3: return CreateInt3(arguments);
|
||||
case 4: return CreateInt4(arguments);
|
||||
case 8: return CreateInt8(arguments);
|
||||
case 16: return CreateInt16(arguments);
|
||||
}
|
||||
return new KalkVectorConstructor<int>(dimension).Invoke(Engine, arguments);
|
||||
case "bool":
|
||||
switch (dimension)
|
||||
{
|
||||
case 2: return CreateBool2(arguments);
|
||||
case 3: return CreateBool3(arguments);
|
||||
case 4: return CreateBool4(arguments);
|
||||
case 8: return CreateBool8(arguments);
|
||||
case 16: return CreateBool16(arguments);
|
||||
}
|
||||
return new KalkVectorConstructor<KalkBool>(dimension).Invoke(Engine, arguments);
|
||||
|
||||
case "float":
|
||||
switch (dimension)
|
||||
{
|
||||
case 2: return CreateFloat2(arguments);
|
||||
case 3: return CreateFloat3(arguments);
|
||||
case 4: return CreateFloat4(arguments);
|
||||
case 8: return CreateFloat8(arguments);
|
||||
case 16: return CreateFloat16(arguments);
|
||||
}
|
||||
return new KalkVectorConstructor<float>(dimension).Invoke(Engine, arguments);
|
||||
|
||||
case "half":
|
||||
switch (dimension)
|
||||
{
|
||||
case 2: return CreateHalf2(arguments);
|
||||
case 3: return CreateHalf3(arguments);
|
||||
case 4: return CreateHalf4(arguments);
|
||||
case 8: return CreateHalf8(arguments);
|
||||
case 16: return CreateHalf16(arguments);
|
||||
case 32: return CreateHalf32(arguments);
|
||||
}
|
||||
return new KalkVectorConstructor<KalkHalf>(dimension).Invoke(Engine, arguments);
|
||||
|
||||
case "double":
|
||||
switch (dimension)
|
||||
{
|
||||
case 2: return CreateDouble2(arguments);
|
||||
case 3: return CreateDouble3(arguments);
|
||||
case 4: return CreateDouble4(arguments);
|
||||
case 8: return CreateDouble8(arguments);
|
||||
}
|
||||
return new KalkVectorConstructor<double>(dimension).Invoke(Engine, arguments);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Unsupported vector type {name.Name}. Only bool, int, float and double are supported", nameof(name));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates an rgb vector type with the specified argument values.
|
||||
/// </summary>
|
||||
/// <param name="arguments">The vector item values. The total number of values must equal the dimension of the vector (3). The arguments can be:
|
||||
/// - No values: All items of the rgb vector are initialized with the value 0.
|
||||
/// - an integer value: `rgb(0xAABBCC)` will extract the RGB 8-bits component values (AA: R, BB: G, CC: B).
|
||||
/// - a string value: `rgb("#AABBCC")` or `rgb("AABBCC")` will extract the RGB 8-bits component values (AA: R, BB: G, CC: B).
|
||||
/// - an array value: `rgb([0xAA,0xBB,0xCC])` will initialize rgb elements with the array elements. The size of the array must match the size of the rgb vector (3).
|
||||
/// - A combination of vectors/single values (e.g `rgb(float3(0.1, 0.2, 0.3)`).
|
||||
/// </param>
|
||||
/// <returns>A rgb vector initialized with the specified arguments</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> rgb(0xAABBCC)
|
||||
/// # rgb(11189196)
|
||||
/// out = rgb(170, 187, 204) ## AABBCC ##
|
||||
/// >>> rgb("#AABBCC")
|
||||
/// # rgb("#AABBCC")
|
||||
/// out = rgb(170, 187, 204) ## AABBCC ##
|
||||
/// >>> rgb("AABBCC")
|
||||
/// # rgb("AABBCC")
|
||||
/// out = rgb(170, 187, 204) ## AABBCC ##
|
||||
/// >>> rgb([0xAA,0xBB,0xCC])
|
||||
/// # rgb([170,187,204])
|
||||
/// out = rgb(170, 187, 204) ## AABBCC ##
|
||||
/// >>> out.xyz
|
||||
/// # out.xyz
|
||||
/// out = float3(0.6666667, 0.73333335, 0.8)
|
||||
/// >>> rgb(out)
|
||||
/// # rgb(out)
|
||||
/// out = rgb(170, 187, 204) ## AABBCC ##
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("rgb", CategoryVectorTypeConstructors)]
|
||||
public KalkColorRgb CreateRgb(params object[] arguments) => (KalkColorRgb)RgbConstructor.Invoke(Engine, arguments);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an rgba vector type with the specified argument values.
|
||||
/// </summary>
|
||||
/// <param name="arguments">The vector item values. The total number of values must equal the dimension of the vector (4). The arguments can be:
|
||||
/// - No values: All items of the rgba vector are initialized with the value 0.
|
||||
/// - an integer value: `rgba(0xFFAABBCC)` will extract the RGB 8-bits component values (FF: A, AA: R, BB: G, CC: B).
|
||||
/// - a string value: `rgba("#FFAABBCC")` or `rgba("FFAABBCC")` will extract the RGB 8-bits component values (FF: A, AA: R, BB: G, CC: B).
|
||||
/// - an array value: `rgba([0xAA,0xBB,0xCC,0xFF])` will initialize rgba elements with the array elements. The size of the array must match the size of the rgb vector (3).
|
||||
/// - A combination of vectors/single values (e.g `rgba(float4(0.1, 0.2, 0.3, 1.0)`).
|
||||
/// </param>
|
||||
/// <returns>A rgb vector initialized with the specified arguments</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> rgba(0xFFAABBCC)
|
||||
/// # rgba(-5588020)
|
||||
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
|
||||
/// >>> rgba("#FFAABBCC")
|
||||
/// # rgba("#FFAABBCC")
|
||||
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
|
||||
/// >>> rgba("FFAABBCC")
|
||||
/// # rgba("FFAABBCC")
|
||||
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
|
||||
/// >>> rgba([0xAA,0xBB,0xCC,0xFF])
|
||||
/// # rgba([170,187,204,255])
|
||||
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
|
||||
/// >>> out.xyzw
|
||||
/// # out.xyzw
|
||||
/// out = float4(0.6666667, 0.73333335, 0.8, 1)
|
||||
/// >>> rgba(out)
|
||||
/// # rgba(out)
|
||||
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("rgba", CategoryVectorTypeConstructors)]
|
||||
public KalkColorRgba CreateRgba(params object[] arguments) => (KalkColorRgba)RgbaConstructor.Invoke(Engine, arguments);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix of the specified element type, number of rows and columns and optional values.
|
||||
/// </summary>
|
||||
/// <param name="name">The element type of the matrix (e.g float).</param>
|
||||
/// <param name="row">The number of rows.</param>
|
||||
/// <param name="column">The number of columns.</param>
|
||||
/// <param name="arguments">The optional values (must have 1 or row x column elements).</param>
|
||||
/// <returns>A matrix of the specified row x column.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> matrix(float,4,3,1..12)
|
||||
/// # matrix(float, 4, 3, 1..12)
|
||||
/// out = float4x3(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
|
||||
/// # col 0 1 2 / row
|
||||
/// float3(1 , 2 , 3 ) # 0
|
||||
/// float3(4 , 5 , 6 ) # 1
|
||||
/// float3(7 , 8 , 9 ) # 2
|
||||
/// float3(10 , 11 , 12 ) # 3
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("matrix", CategoryMatrixConstructors)]
|
||||
public object CreateMatrix(ScriptVariable name, int row, int column, params object[] arguments)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (row <= 1) throw new ArgumentOutOfRangeException(nameof(row), $"Invalid row count {row}. Expecting a value > 1.");
|
||||
if (column <= 1) throw new ArgumentOutOfRangeException(nameof(column), $"Invalid column count {column}. Expecting a value > 1.");
|
||||
switch (name.Name)
|
||||
{
|
||||
case "int":
|
||||
return new KalkMatrixConstructor<int>(row, column).Invoke(Engine, arguments);
|
||||
case "bool":
|
||||
return new KalkMatrixConstructor<KalkBool>(row, column).Invoke(Engine, arguments);
|
||||
case "float":
|
||||
return new KalkMatrixConstructor<float>(row, column).Invoke(Engine, arguments);
|
||||
case "half":
|
||||
return new KalkMatrixConstructor<KalkHalf>(row, column).Invoke(Engine, arguments);
|
||||
case "double":
|
||||
return new KalkMatrixConstructor<double>(row, column).Invoke(Engine, arguments);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Unsupported matrix type {name.Name}. Only bool, int, float, half and double are supported", nameof(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
1819
Kalk/Kalk.Core/Modules/Vectors/TypesModule.generated.cs
Normal file
1819
Kalk/Kalk.Core/Modules/Vectors/TypesModule.generated.cs
Normal file
File diff suppressed because it is too large
Load Diff
131
Kalk/Kalk.Core/Modules/Vectors/TypesModule.tt
Normal file
131
Kalk/Kalk.Core/Modules/Vectors/TypesModule.tt
Normal file
@@ -0,0 +1,131 @@
|
||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ output extension=".generated.cs" #>
|
||||
<#
|
||||
var types = new List<(string, string, int, int, string)>();
|
||||
|
||||
foreach(var typeU in new string[] { "Bool", "Int", "Float", "Double", "Half" })
|
||||
{
|
||||
var type = typeU.ToLowerInvariant();
|
||||
for(int y = 2; y <= 4; y++) {
|
||||
for(int x = 2; x <= 4; x++) {
|
||||
var genericType = typeU == "Bool" ? "KalkBool" : typeU == "Half" ? "KalkHalf" : type;
|
||||
types.Add((type, $"{typeU}{y}x{x}", y, x, genericType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vectypes = new List<(string, string, string, int[])>() {
|
||||
("byte", "Byte", "byte", new int[] {16,32,64}),
|
||||
("sbyte", "SByte", "sbyte", new int[] {16,32,64}),
|
||||
("short", "Short", "short", new int[] {2,4,8,16,32}),
|
||||
("ushort", "UShort", "ushort", new int[] {2,4,8,16,32}),
|
||||
("int", "Int", "int", new int[] {2,3,4,8,16}),
|
||||
("uint", "UInt", "uint", new int[] {2,3,4,8,16}),
|
||||
("long", "Long", "long", new int[] {2,3,4,8}),
|
||||
("ulong", "ULong", "ulong", new int[] {2,3,4,8}),
|
||||
("KalkBool", "Bool", "bool", new int[] {2,3,4,8,16}),
|
||||
("float", "Float", "float", new int[] {2,3,4,8,16}),
|
||||
("double", "Double", "double", new int[] {2,3,4,8}),
|
||||
("KalkHalf", "Half", "half", new int[] {2,3,4,8,16,32}),
|
||||
};
|
||||
#>
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Date: <#= DateTime.Now #>
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
using System;
|
||||
using Scriban.Helpers;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
public partial class TypesModule
|
||||
{
|
||||
private const string CategoryMatrixConstructors = "Type Matrix Constructors";
|
||||
|
||||
<# foreach(var type in vectypes) { #>
|
||||
<# foreach(var dim in type.Item4) { #>
|
||||
private static readonly KalkVectorConstructor<<#= type.Item1 #>> <#= type.Item2 #><#= dim #>Constructor = new KalkVectorConstructor<<#= type.Item1 #>>(<#= dim #>);
|
||||
<# } #>
|
||||
<# } #>
|
||||
|
||||
<# foreach(var type in types) { #>
|
||||
private static readonly KalkMatrixConstructor<<#= type.Item5 #>> <#= type.Item2 #>Constructor = new KalkMatrixConstructor<<#= type.Item5 #>>(<#= type.Item3 #>, <#= type.Item4 #>);
|
||||
<# } #>
|
||||
|
||||
<# foreach(var type in vectypes) { #>
|
||||
<# foreach(var dim in type.Item4) {
|
||||
var ctor = $"{type.Item3}{dim}";
|
||||
if (type.Item3 == "bool") { #>
|
||||
/// <summary>Creates a vector of <#= dim #> `<#= type.Item3 #>` items.</summary>
|
||||
/// <param name="arguments">The vector item values. The total number of values must equal the dimension of the vector. The arguments can be:
|
||||
/// - No values: The vector is initialized with false values.
|
||||
/// - a single value: `<#= ctor #>(true)` will initialize all elements with 123.
|
||||
/// - an array value: `<#= ctor #>([true, false, ...])` will initialize all elements with the array elements. The size of the array must match the size of the vector.
|
||||
/// - A combination of vectors/single values (e.g `<#= type.Item3 #>4(<#= type.Item3 #>2(true,false), <#= type.Item3 #>2(false,true))` or `<#= type.Item3 #>4(<#= type.Item3 #>3(false,true,true), false)`)
|
||||
/// </param>
|
||||
/// <returns>A <#= ctor #> vector initialized with the specified arguments</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> <#= ctor #>(true)
|
||||
/// # <#= ctor #>(true)
|
||||
/// out = <#= ctor #>(<#= string.Join(", ", Enumerable.Range(1, dim).Select(x => "true")) #>)
|
||||
<# } else { #>
|
||||
/// <summary>Creates a vector of <#= dim #> `<#= type.Item3 #>` items.</summary>
|
||||
/// <param name="arguments">The vector item values. The total number of values must equal the dimension of the vector. The arguments can be:
|
||||
/// - No values: All items of the vector are initialized with the value 0.
|
||||
/// - a single value: `<#= ctor #>(123)` will initialize all elements with 123.
|
||||
/// - an array value: `<#= ctor #>(1..<#= dim #>)` will initialize all elements with the array elements. The size of the array must match the size of the vector.
|
||||
/// - A combination of vectors/single values (e.g `<#= type.Item3 #>4(<#= type.Item3 #>2(1,2), <#= type.Item3 #>2(3,4))` or `<#= type.Item3 #>4(<#= type.Item3 #>3(1,2,3), 4)`.
|
||||
/// </param>
|
||||
/// <returns>A <#= ctor #> vector initialized with the specified arguments</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> <#= ctor #>
|
||||
/// # <#= ctor #>
|
||||
/// out = <#= ctor #>(<#= string.Join(", ", Enumerable.Range(1, dim).Select(x => "0")) #>)
|
||||
/// >>> <#= ctor #>(1..<#= dim #>)
|
||||
/// # <#= ctor #>(1..<#= dim #>)
|
||||
/// out = <#= ctor #>(<#= string.Join(", ", Enumerable.Range(1, dim)) #>)
|
||||
<# if (dim <= 16) { #>
|
||||
/// >>> <#= ctor #>(<#= string.Join(", ", Enumerable.Range(10, dim)) #>)
|
||||
/// # <#= ctor #>(<#= string.Join(", ", Enumerable.Range(10, dim)) #>)
|
||||
/// out = <#= ctor #>(<#= string.Join(", ", Enumerable.Range(10, dim)) #>)
|
||||
<# }
|
||||
}
|
||||
#>
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("<#= ctor #>", CategoryVectorTypeConstructors)]
|
||||
public KalkVector<<#= type.Item1 #>> Create<#= type.Item2 #><#= dim #>(params object[] arguments) => <#= type.Item2 #><#= dim #>Constructor.Invoke(Engine, arguments);
|
||||
<# }
|
||||
} #>
|
||||
|
||||
|
||||
<# foreach(var type in types) {
|
||||
var dim = type.Item3 * type.Item4;
|
||||
var ctor_prefix = type.Item1.ToLowerInvariant();
|
||||
var ctor = type.Item2.ToLowerInvariant();
|
||||
#>
|
||||
/// <summary>Creates a <#= type.Item3 #> (rows) x <#= type.Item4 #> (columns) matrix of <#= type.Item1 #>.</summary>
|
||||
/// <param name="arguments">The matrix item values. The total number of values must equal the total dimension of the matrix. The arguments can be:
|
||||
/// - No values: All items of the vector are initialized with the value 0.
|
||||
/// - a single value: `<#= ctor #>(123)` will initialize all elements with 123.
|
||||
/// - an array value: `<#= ctor #>(1..<#= dim #>)` will initialize all elements with the array elements. The size of the array must match the size of the vector.
|
||||
/// - A combination of vectors/single values (e.g `<#= ctor_prefix #>3x4(<#= ctor_prefix #>4(1), <#= ctor_prefix #>4(2), <#= ctor_prefix #>4(3))`.
|
||||
/// </param>
|
||||
/// <returns>A <#= ctor #> matrix initialized with the specified arguments</returns>
|
||||
[KalkExport("<#= ctor #>", CategoryMatrixConstructors)]
|
||||
public KalkMatrix<<#= type.Item5 #>> Create<#= type.Item2 #>(params object[] arguments) => <#= type.Item2 #>Constructor.Invoke(Engine, arguments);
|
||||
<# } #>
|
||||
}
|
||||
}
|
||||
228
Kalk/Kalk.Core/Modules/Vectors/VectorModule.Matrics.cs
Normal file
228
Kalk/Kalk.Core/Modules/Vectors/VectorModule.Matrics.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
public partial class VectorModule
|
||||
{
|
||||
/// <summary>
|
||||
/// Transposes the specified matrix.
|
||||
/// </summary>
|
||||
/// <param name="m">The matrix to transpose.</param>
|
||||
/// <returns>The transposed matrix.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> transpose float3x4(1..12)
|
||||
/// # transpose(float3x4(1..12))
|
||||
/// out = float4x3(1, 5, 9, 2, 6, 10, 3, 7, 11, 4, 8, 12)
|
||||
/// # col 0 1 2 / row
|
||||
/// float3(1 , 5 , 9 ) # 0
|
||||
/// float3(2 , 6 , 10 ) # 1
|
||||
/// float3(3 , 7 , 11 ) # 2
|
||||
/// float3(4 , 8 , 12 ) # 3
|
||||
/// >>> transpose(out)
|
||||
/// # transpose(out)
|
||||
/// out = float3x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
|
||||
/// # col 0 1 2 3 / row
|
||||
/// float4(1 , 2 , 3 , 4 ) # 0
|
||||
/// float4(5 , 6 , 7 , 8 ) # 1
|
||||
/// float4(9 , 10 , 11 , 12 ) # 2
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("transpose", CategoryMathVectorMatrixFunctions)]
|
||||
public static KalkMatrix Transpose(KalkMatrix m) => KalkMatrix.Transpose(m);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an identity of a squared matrix.
|
||||
/// </summary>
|
||||
/// <param name="m">The type of the squared matrix.</param>
|
||||
/// <returns>The identity matrix of the squared matrix type.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> identity(float4x4)
|
||||
/// # identity(float4x4)
|
||||
/// out = float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
||||
/// # col 0 1 2 3 / row
|
||||
/// float4(1 , 0 , 0 , 0 ) # 0
|
||||
/// float4(0 , 1 , 0 , 0 ) # 1
|
||||
/// float4(0 , 0 , 1 , 0 ) # 2
|
||||
/// float4(0 , 0 , 0 , 1 ) # 3
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("identity", CategoryMathVectorMatrixFunctions)]
|
||||
public static KalkMatrix Identity(KalkMatrix m) => KalkMatrix.Identity(m);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the determinant of the specified matrix.
|
||||
/// </summary>
|
||||
/// <param name="m">The matrix to calculate the determinant for.</param>
|
||||
/// <returns>A scalar representing the determinant of the matrix.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> float4x4(4,3,2,2,0,1,-3,3,0,-1,3,3,0,3,1,1)
|
||||
/// # float4x4(4, 3, 2, 2, 0, 1, -3, 3, 0, -1, 3, 3, 0, 3, 1, 1)
|
||||
/// out = float4x4(4, 3, 2, 2, 0, 1, -3, 3, 0, -1, 3, 3, 0, 3, 1, 1)
|
||||
/// # col 0 1 2 3 / row
|
||||
/// float4(4 , 3 , 2 , 2 ) # 0
|
||||
/// float4(0 , 1 , -3 , 3 ) # 1
|
||||
/// float4(0 , -1 , 3 , 3 ) # 2
|
||||
/// float4(0 , 3 , 1 , 1 ) # 3
|
||||
/// >>> determinant out
|
||||
/// # determinant(out)
|
||||
/// out = -240
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("determinant", CategoryMathVectorMatrixFunctions)]
|
||||
public static object Determinant(KalkMatrix m) => KalkMatrix.Determinant(m);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the inverse of the specified matrix.
|
||||
/// </summary>
|
||||
/// <param name="m">The matrix to calculate the inverse for.</param>
|
||||
/// <returns>The inverse matrix of the specified matrix.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> inverse(float3x3(10,20,10,4,5,6,2,3,5))
|
||||
/// # inverse(float3x3(10, 20, 10, 4, 5, 6, 2, 3, 5))
|
||||
/// out = float3x3(-0.1, 1, -1, 0.11428571, -0.42857143, 0.28571427, -0.028571427, -0.14285715, 0.42857143)
|
||||
/// # col 0 1 2 / row
|
||||
/// float3(-0.1 , 1 , -1 ) # 0
|
||||
/// float3( 0.11428571 , -0.42857143, 0.28571427) # 1
|
||||
/// float3(-0.028571427, -0.14285715, 0.42857143) # 2
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("inverse", CategoryMathVectorMatrixFunctions)]
|
||||
public static KalkMatrix Inverse(KalkMatrix m) => KalkMatrix.Inverse(m);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the diagonal vector of a squared matrix or a diagonal matrix from the specified vector.
|
||||
/// </summary>
|
||||
/// <param name="x">A vector or matrix to return the associated diagonal for.</param>
|
||||
/// <returns>A diagonal vector of a matrix or a diagonal matrix of a vector.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> diag(float4x4(1..16))
|
||||
/// # diag(float4x4(1..16))
|
||||
/// out = float4(1, 6, 11, 16)
|
||||
/// >>> diag(float4(1,2,3,4))
|
||||
/// # diag(float4(1, 2, 3, 4))
|
||||
/// out = float4x4(1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4)
|
||||
/// # col 0 1 2 3 / row
|
||||
/// float4(1 , 0 , 0 , 0 ) # 0
|
||||
/// float4(0 , 2 , 0 , 0 ) # 1
|
||||
/// float4(0 , 0 , 3 , 0 ) # 2
|
||||
/// float4(0 , 0 , 0 , 4 ) # 3
|
||||
///
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("diag", CategoryMathVectorMatrixFunctions)]
|
||||
public object Diagonal(object x)
|
||||
{
|
||||
if (x == null) throw new ArgumentNullException(nameof(x));
|
||||
|
||||
if (x is KalkMatrix m)
|
||||
{
|
||||
return KalkMatrix.Diagonal(m);
|
||||
}
|
||||
|
||||
if (x is KalkVector v)
|
||||
{
|
||||
return KalkVector.Diagonal(v);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid argument type {Engine.GetTypeName(x)}. Expecting a matrix or a vector type.", nameof(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a row from the specified matrix.
|
||||
/// </summary>
|
||||
/// <param name="x">The matrix to extract a row from.</param>
|
||||
/// <param name="index">The index of the row (zero based).</param>
|
||||
/// <returns>A vector extracted from the matrix.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> row(float4x4(1..16), 2)
|
||||
/// # row(float4x4(1..16), 2)
|
||||
/// out = float4(9, 10, 11, 12)
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("row", CategoryMathVectorMatrixFunctions)]
|
||||
public KalkVector GetRow(KalkMatrix x, int index)
|
||||
{
|
||||
return x.GetRow(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a column from the specified matrix.
|
||||
/// </summary>
|
||||
/// <param name="x">The matrix to extract a column from.</param>
|
||||
/// <param name="index">The index of the column (zero based).</param>
|
||||
/// <returns>A vector extracted from the matrix.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> col(float4x4(1..16), 2)
|
||||
/// # col(float4x4(1..16), 2)
|
||||
/// out = float4(3, 7, 11, 15)
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("col", CategoryMathVectorMatrixFunctions)]
|
||||
public KalkVector GetColumn(KalkMatrix x, int index)
|
||||
{
|
||||
return x.GetColumn(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector x vector (dot product), or a vector x matrix, or a matrix x vector or a matrix x matrix.
|
||||
/// </summary>
|
||||
/// <param name="x">A left vector or a matrix.</param>
|
||||
/// <param name="y">A right vector or matrix.</param>
|
||||
/// <returns>The result of the multiplication.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> mul(float4(1,2,3,4), float4(5,6,7,8))
|
||||
/// # mul(float4(1, 2, 3, 4), float4(5, 6, 7, 8))
|
||||
/// out = 70
|
||||
/// >>> mul(float3(3,7,5), float3x3(2,3,-4,11,8,7,2,5,3))
|
||||
/// # mul(float3(3, 7, 5), float3x3(2, 3, -4, 11, 8, 7, 2, 5, 3))
|
||||
/// out = float3(7, 124, 56)
|
||||
/// >>> mul(float3x3(2,3,-4,11,8,7,2,5,3), float3(3,7,5))
|
||||
/// # mul(float3x3(2, 3, -4, 11, 8, 7, 2, 5, 3), float3(3, 7, 5))
|
||||
/// out = float3(93, 90, 52)
|
||||
/// >>> mul(float3x3(2,7,4,3,2,1,9,-1,2), float3x3(1,4,6,-1,-2,5,8,7,6))
|
||||
/// # mul(float3x3(2, 7, 4, 3, 2, 1, 9, -1, 2), float3x3(1, 4, 6, -1, -2, 5, 8, 7, 6))
|
||||
/// out = float3x3(68, 9, 20, 37, -16, 4, 91, 64, 51)
|
||||
/// # col 0 1 2 / row
|
||||
/// float3(68 , 9 , 20 ) # 0
|
||||
/// float3(37 , -16 , 4 ) # 1
|
||||
/// float3(91 , 64 , 51 ) # 2
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("mul", CategoryMathVectorMatrixFunctions)]
|
||||
public object Multiply(object x, object y)
|
||||
{
|
||||
if (x == null) throw new ArgumentNullException(nameof(x));
|
||||
if (y == null) throw new ArgumentNullException(nameof(y));
|
||||
|
||||
if (x is KalkVector vx && y is KalkVector vy)
|
||||
{
|
||||
return KalkVector.Dot(vx, vy);
|
||||
}
|
||||
|
||||
if (x is KalkVector vx1 && y is KalkMatrix my)
|
||||
{
|
||||
return KalkMatrix.Multiply(vx1, my);
|
||||
}
|
||||
if (x is KalkMatrix mx && y is KalkVector vy1)
|
||||
{
|
||||
return KalkMatrix.Multiply(mx, vy1);
|
||||
}
|
||||
|
||||
if (x is KalkMatrix mx1 && y is KalkMatrix my2)
|
||||
{
|
||||
return KalkMatrix.Multiply(mx1, my2);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Unsupported type for matrix multiplication. The combination of {Engine.GetTypeName(x)} * {Engine.GetTypeName(y)} is not supported.", nameof(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
140
Kalk/Kalk.Core/Modules/Vectors/VectorModule.cs
Normal file
140
Kalk/Kalk.Core/Modules/Vectors/VectorModule.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using Scriban.Syntax;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
public sealed partial class VectorModule : KalkModuleWithFunctions
|
||||
{
|
||||
public const string CategoryMathVectorMatrixFunctions = "Math Vector/Matrix Functions";
|
||||
private static readonly KalkColorRgbConstructor RgbConstructor = new KalkColorRgbConstructor();
|
||||
private static readonly KalkColorRgbaConstructor RgbaConstructor = new KalkColorRgbaConstructor();
|
||||
private MathModule _mathModule;
|
||||
|
||||
public VectorModule() : base("Vectors")
|
||||
{
|
||||
IsBuiltin = true;
|
||||
RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
_mathModule = Engine.GetOrCreateModule<MathModule>();
|
||||
//Engine.LoadSystemFile(Path.Combine("Modules", "Vectors", "colorspaces.kalk"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the specified floating-point vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The specified floating-point vector.</param>
|
||||
/// <returns>A floating-point scalar that represents the length of the x parameter.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> length float2(1, 2)
|
||||
/// # length(float2(1, 2))
|
||||
/// out = 2.23606797749979
|
||||
/// >>> length(-5)
|
||||
/// # length(-5)
|
||||
/// out = 5
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("length", CategoryMathVectorMatrixFunctions)]
|
||||
public object Length(object x)
|
||||
{
|
||||
if (x == null) throw new ArgumentNullException(nameof(x));
|
||||
if (_mathModule == null) throw new InvalidOperationException($"The module {Name} is not initialized.");
|
||||
if (x is KalkVector v)
|
||||
{
|
||||
return _mathModule.Sqrt(new KalkDoubleValue(KalkVector.Dot(v, v)));
|
||||
}
|
||||
return _mathModule.Abs(new KalkCompositeValue(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the specified floating-point vector according to x / length(x).
|
||||
/// </summary>
|
||||
/// <param name="x">he specified floating-point vector.</param>
|
||||
/// <returns>The normalized x parameter. If the length of the x parameter is 0, the result is indefinite.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> normalize float2(1,2)
|
||||
/// # normalize(float2(1, 2))
|
||||
/// out = float2(0.4472136, 0.8944272)
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("normalize", CategoryMathVectorMatrixFunctions)]
|
||||
public object Normalize(object x)
|
||||
{
|
||||
return ScriptBinaryExpression.Evaluate(Engine, Engine.CurrentSpan, ScriptBinaryOperator.Divide, x, Length(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the dot product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="x">The first vector.</param>
|
||||
/// <param name="y">The second vector.</param>
|
||||
/// <returns>The dot product of the x parameter and the y parameter.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> dot(float3(1,2,3), float3(4,5,6))
|
||||
/// # dot(float3(1, 2, 3), float3(4, 5, 6))
|
||||
/// out = 32
|
||||
/// >>> dot(float3(1,2,3), 4)
|
||||
/// # dot(float3(1, 2, 3), 4)
|
||||
/// out = 24
|
||||
/// >>> dot(4, float3(1,2,3))
|
||||
/// # dot(4, float3(1, 2, 3))
|
||||
/// out = 24
|
||||
/// >>> dot(5,6)
|
||||
/// # dot(5, 6)
|
||||
/// out = 30
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("dot", CategoryMathVectorMatrixFunctions)]
|
||||
public object Dot(object x, object y)
|
||||
{
|
||||
if (x is KalkVector vx)
|
||||
{
|
||||
if (y is KalkVector vy)
|
||||
{
|
||||
return KalkVector.Dot(vx, vy);
|
||||
}
|
||||
return KalkVector.Dot(vx, vx.FromValue(Engine.ToObject(1, y, vx.ElementType)));
|
||||
}
|
||||
else if (y is KalkVector vy)
|
||||
{
|
||||
return KalkVector.Dot(vy.FromValue(Engine.ToObject(1, x, vy.ElementType)), vy);
|
||||
}
|
||||
|
||||
return ScriptBinaryExpression.Evaluate(Engine, Engine.CurrentSpan, ScriptBinaryOperator.Multiply, x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cross product of two floating-point, 3D vectors.
|
||||
/// </summary>
|
||||
/// <param name="x">The first floating-point, 3D vector.</param>
|
||||
/// <param name="y">The second floating-point, 3D vector.</param>
|
||||
/// <returns>The cross product of the x parameter and the y parameter.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> cross(float3(1,2,3), float3(4,5,6))
|
||||
/// # cross(float3(1, 2, 3), float3(4, 5, 6))
|
||||
/// out = float3(-3, 6, -3)
|
||||
/// >>> cross(float3(1,0,0), float3(0,1,0))
|
||||
/// # cross(float3(1, 0, 0), float3(0, 1, 0))
|
||||
/// out = float3(0, 0, 1)
|
||||
/// >>> cross(float3(0,0,1), float3(0,1,0))
|
||||
/// # cross(float3(0, 0, 1), float3(0, 1, 0))
|
||||
/// out = float3(-1, 0, 0)
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("cross", CategoryMathVectorMatrixFunctions)]
|
||||
public object Cross(KalkVector x, KalkVector y)
|
||||
{
|
||||
if (x == null) throw new ArgumentNullException(nameof(x));
|
||||
if (y == null) throw new ArgumentNullException(nameof(y));
|
||||
if (x.Length != 3 || (x.ElementType != typeof(float) && x.ElementType != typeof(double))) throw new ArgumentOutOfRangeException(nameof(x), "Expecting a float3 or double3 vector.");
|
||||
if (y.Length != 3 || (y.ElementType != typeof(float) && y.ElementType != typeof(double))) throw new ArgumentOutOfRangeException(nameof(y), "Expecting a float3 or double3 vector.");
|
||||
return KalkVector.Cross(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
153
Kalk/Kalk.Core/Modules/Vectors/colorspaces.kalk
Normal file
153
Kalk/Kalk.Core/Modules/Vectors/colorspaces.kalk
Normal file
@@ -0,0 +1,153 @@
|
||||
# From http://www.chilliant.com/rgb2hsv.html
|
||||
|
||||
# Based on work by Sam Hocevar and Emil Persson
|
||||
func HUEtoRGB(x)
|
||||
$r = abs(x * 6 - 3) - 1;
|
||||
$g = 2 - abs(x * 6 - 2);
|
||||
$b = 2 - abs(x * 6 - 4);
|
||||
ret saturate(float3($r, $g, $b));
|
||||
end
|
||||
|
||||
func RGBtoHCV(x)
|
||||
x = x.xyz;
|
||||
Epsilon = 1e-10f
|
||||
if x.g < x.b; $p =float4(x.bg, -1.0f, 2.0f/3.0f); else; $p = float4(x.gb, 0.0f, -1.0f/3.0f); end;
|
||||
if x.r < $p.x; $q = float4($p.xyw, x.r); else; $q = float4(x.r, $p.yzx); end;
|
||||
$c = $q.x - min($q.w, $q.y);
|
||||
$h = abs(($q.w - $q.y) / (6.0f * $c + Epsilon) + $q.z);
|
||||
ret float3($h, $c, $q.x);
|
||||
end
|
||||
|
||||
func HSVtoRGB(x)
|
||||
x = x.xyz;
|
||||
$rgb = HUEtoRGB(x.x);
|
||||
ret (($rgb - 1) * x.y + 1) * x.z;
|
||||
end
|
||||
|
||||
func HSLtoRGB(x)
|
||||
x = x.xyz;
|
||||
$rgb = HUEtoRGB(x.x);
|
||||
$c = (1 - abs(2 * x.z - 1)) * x.y;
|
||||
ret ($rgb - 0.5) * $c + x.z;
|
||||
end
|
||||
|
||||
func HCYtoRGB(x)
|
||||
x = x.xyz;
|
||||
# The weights of RGB contributions to luminance.
|
||||
# Should sum to unity.
|
||||
$HCYwts = float3(0.299f, 0.587f, 0.114f);
|
||||
$rgb = HUEtoRGB(x.x);
|
||||
$z = dot($rgb, $HCYwts);
|
||||
if x.z < $z
|
||||
x.y = x.y * x.z / $z;
|
||||
else if $z < 1
|
||||
x.y = x.y * (1 - x.z) / (1 - $z);
|
||||
end
|
||||
ret ($rgb - $z) * x.y + x.z;
|
||||
end
|
||||
|
||||
func HCLtoRGB(x)
|
||||
x = x.xyz;
|
||||
HCLgamma = 3;
|
||||
HCLy0 = 100;
|
||||
HCLmaxL = 0.530454533953517; # == exp(HCLgamma / HCLy0) - 0.5
|
||||
$rgb = float3;
|
||||
if x.z != 0
|
||||
H = x.x;
|
||||
C = x.y;
|
||||
L = x.z * HCLmaxL;
|
||||
Q = exp((1 - C / (2 * L)) * (HCLgamma / HCLy0));
|
||||
U = (2 * L - C) / (2 * Q - 1);
|
||||
V = C / Q;
|
||||
A = (H + min(frac(2 * H) / 4, frac(-2 * H) / 8)) * pi * 2;
|
||||
H = H * 6;
|
||||
if (H <= 0.999)
|
||||
T = tan(A);
|
||||
$rgb.r = 1;
|
||||
$rgb.g = T / (1 + T);
|
||||
else if (H <= 1.001)
|
||||
$rgb.r = 1;
|
||||
$rgb.g = 1;
|
||||
else if (H <= 2)
|
||||
T = tan(A);
|
||||
$rgb.r = (1 + T) / T;
|
||||
$rgb.g = 1;
|
||||
else if (H <= 3)
|
||||
T = tan(A);
|
||||
$rgb.g = 1;
|
||||
$rgb.b = 1 + T;
|
||||
else if (H <= 3.999)
|
||||
T = tan(A);
|
||||
$rgb.g = 1 / (1 + T);
|
||||
$rgb.b = 1;
|
||||
else if (H <= 4.001)
|
||||
$rgb.g = 0;
|
||||
$rgb.b = 1;
|
||||
else if (H <= 5)
|
||||
T = tan(A);
|
||||
$rgb.r = -1 / T;
|
||||
$rgb.b = 1;
|
||||
else
|
||||
T = tan(A);
|
||||
$rgb.r = 1;
|
||||
$rgb.b = -T;
|
||||
end
|
||||
$rgb = $rgb * V + U;
|
||||
end
|
||||
ret $rgb;
|
||||
end
|
||||
|
||||
func RGBtoHSV(x)
|
||||
x = x.xyz;
|
||||
Epsilon = 1e-10f
|
||||
$Hcv = RGBtoHCV(x);
|
||||
$s = $Hcv.y / ($Hcv.z + Epsilon);
|
||||
ret float3($Hcv.x, $s, $Hcv.z);
|
||||
end
|
||||
|
||||
func RGBtoHSL(x)
|
||||
x = x.xyz;
|
||||
Epsilon = 1e-10f
|
||||
$hcv = RGBtoHCV(x);
|
||||
$l = $hcv.z - $hcv.y * 0.5;
|
||||
$s = $hcv.y / (1 - abs($l * 2 - 1) + Epsilon);
|
||||
ret float3($hcv.x, $s, $l);
|
||||
end
|
||||
|
||||
func RGBtoHCY(x)
|
||||
x = x.xyz;
|
||||
# Corrected by David Schaeffer
|
||||
$HCYwts = float3(0.299f, 0.587f, 0.114f);
|
||||
Epsilon = 1e-10f
|
||||
$hcv = RGBtoHCV(x);
|
||||
$y = dot(x, $HCYwts);
|
||||
$z = dot(HUEtoRGB($hcv.x), $HCYwts);
|
||||
if ($y < $z)
|
||||
$hcv.y = $hcv.y * $z / (Epsilon + $y);
|
||||
else
|
||||
$hcv.y = $hcv.y * (1 - $z) / (Epsilon + 1 - $y);
|
||||
end
|
||||
ret float3($hcv.x, $hcv.y, $y);
|
||||
end
|
||||
|
||||
func RGBtoHCL(x)
|
||||
x = x.xyz;
|
||||
HCLgamma = 3;
|
||||
HCLy0 = 100;
|
||||
HCLmaxL = 0.530454533953517; # == exp(HCLgamma / HCLy0) - 0.5
|
||||
$hcl = float3
|
||||
H = 0;
|
||||
U = min(x.r, min(x.g, x.b));
|
||||
V = max(x.r, max(x.g, x.b));
|
||||
Q = HCLgamma / HCLy0;
|
||||
$hcl.y = V - U;
|
||||
if ($hcl.y != 0)
|
||||
H = atan2(x.g - x.b, x.r - x.g) / pi;
|
||||
Q = Q * U / V;
|
||||
end
|
||||
Q = exp(Q);
|
||||
$hcl.x = frac(H / 2 - min(frac(H), frac(-H)) / 6);
|
||||
$hcl.y = $hcl.y * Q;
|
||||
$hcl.z = lerp(-U, V, Q) / (HCLmaxL * 2);
|
||||
ret $hcl;
|
||||
end
|
||||
440
Kalk/Kalk.Core/Modules/WebModule.cs
Normal file
440
Kalk/Kalk.Core/Modules/WebModule.cs
Normal file
@@ -0,0 +1,440 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Scriban.Functions;
|
||||
using Scriban.Helpers;
|
||||
using Scriban.Runtime;
|
||||
|
||||
namespace Kalk.Core.Modules
|
||||
{
|
||||
/// <summary>
|
||||
/// Module that provides Web functions (e.g `url_encode`, `json`, `wget`...)
|
||||
/// </summary>
|
||||
[KalkExportModule(ModuleName)]
|
||||
public sealed partial class WebModule : KalkModuleWithFunctions
|
||||
{
|
||||
private const string ModuleName = "Web";
|
||||
public const string CategoryWeb = "Web & Html Functions";
|
||||
|
||||
public WebModule() : base(ModuleName)
|
||||
{
|
||||
RegisterFunctionsAuto();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a specified URL text into a URL-encoded.
|
||||
///
|
||||
/// URL encoding converts characters that are not allowed in a URL into character-entity equivalents.
|
||||
/// For example, when the characters < and > are embedded in a block of text to be transmitted in a URL, they are encoded as %3c and %3e.
|
||||
/// </summary>
|
||||
/// <param name="url">The url text to encode as an URL.</param>
|
||||
/// <returns>An encoded URL.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> url_encode "this<is>an:url and another part"
|
||||
/// # url_encode("this<is>an:url and another part")
|
||||
/// out = "this%3Cis%3Ean%3Aurl+and+another+part"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("url_encode", CategoryWeb)]
|
||||
public string UrlEncode(string url) => WebUtility.UrlEncode(url);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a URL-encoded string into a decoded string.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL to decode.</param>
|
||||
/// <returns>The decoded URL</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> url_decode "this%3Cis%3Ean%3Aurl+and+another+part"
|
||||
/// # url_decode("this%3Cis%3Ean%3Aurl+and+another+part")
|
||||
/// out = "this<is>an:url and another part"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("url_decode", CategoryWeb)]
|
||||
public string UrlDecode(string url) => WebUtility.UrlDecode(url);
|
||||
|
||||
/// <summary>
|
||||
/// Identifies all characters in a string that are not allowed in URLS, and replaces the characters with their escaped variants.
|
||||
/// </summary>
|
||||
/// <param name="url">The input string.</param>
|
||||
/// <returns>The input string url escaped</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "<hello> & <scriban>" |> url_escape
|
||||
/// # "<hello> & <scriban>" |> url_escape
|
||||
/// out = "%3Chello%3E%20&%20%3Cscriban%3E"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("url_escape", CategoryWeb)]
|
||||
public string UrlEscape(string url) => HtmlFunctions.UrlEscape(url);
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a HTML input string (replacing `&` by `&amp;`)
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string with HTML entities.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "<p>This is a paragraph</p>" |> html_encode
|
||||
/// # "<p>This is a paragraph</p>" |> html_encode
|
||||
/// out = "&lt;p&gt;This is a paragraph&lt;/p&gt;"
|
||||
/// >>> out |> html_decode
|
||||
/// # out |> html_decode
|
||||
/// out = "<p>This is a paragraph</p>"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("html_encode", CategoryWeb)]
|
||||
public string HtmlEncode(string text) => HtmlFunctions.Escape(text);
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a HTML input string (replacing `&amp;` by `&`)
|
||||
/// </summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string removed with any HTML entities.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "<p>This is a paragraph</p>" |> html_encode
|
||||
/// # "<p>This is a paragraph</p>" |> html_encode
|
||||
/// out = "&lt;p&gt;This is a paragraph&lt;/p&gt;"
|
||||
/// >>> out |> html_decode
|
||||
/// # out |> html_decode
|
||||
/// out = "<p>This is a paragraph</p>"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("html_decode", CategoryWeb)]
|
||||
public string HtmlDecode(string text)
|
||||
{
|
||||
return string.IsNullOrEmpty(text) ? text : System.Net.WebUtility.HtmlDecode(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts to or from a JSON object depending on the value argument.
|
||||
/// </summary>
|
||||
/// <param name="value">A value argument:
|
||||
/// - If the value is a string, it is expecting this string to be a JSON string and will convert it to the appropriate object.
|
||||
/// - If the value is an array or object, it will convert it to a JSON string representation.
|
||||
/// </param>
|
||||
/// <returns>A JSON string or an object/array depending on the argument.</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> json {a: 1, b: 2, c: [4,5], d: "Hello World"}
|
||||
/// # json({a: 1, b: 2, c: [4,5], d: "Hello World"})
|
||||
/// out = "{\"a\": 1, \"b\": 2, \"c\": [4, 5], \"d\": \"Hello World\"}"
|
||||
/// >>> json out
|
||||
/// # json(out)
|
||||
/// out = {a: 1, b: 2, c: [4, 5], d: "Hello World"}
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("json", CategoryWeb)]
|
||||
public object Json(object value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case string text:
|
||||
try
|
||||
{
|
||||
var jsonDoc = JsonDocument.Parse(text);
|
||||
return ConvertFromJson(jsonDoc.RootElement);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ArgumentException($"Unable to parse input text. Reason: {ex.Message}", nameof(value));
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
var previousLimit = Engine.LimitToString;
|
||||
try
|
||||
{
|
||||
Engine.LimitToString = 0;
|
||||
var builder = new StringBuilder();
|
||||
ConvertToJson(value, builder);
|
||||
return builder.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ArgumentException($"Unable to convert script object input to json text. Reason: {ex.Message}", nameof(value));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Engine.LimitToString = previousLimit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Removes any HTML tags from the input string</summary>
|
||||
/// <param name="text">The input string</param>
|
||||
/// <returns>The input string removed with any HTML tags</returns>
|
||||
/// <example>
|
||||
/// ```kalk
|
||||
/// >>> "<p>This is a paragraph</p>" |> html_strip
|
||||
/// # "<p>This is a paragraph</p>" |> html_strip
|
||||
/// out = "This is a paragraph"
|
||||
/// ```
|
||||
/// </example>
|
||||
[KalkExport("html_strip", CategoryWeb)]
|
||||
public string HtmlStrip(string text) => HtmlFunctions.Strip(Engine, text);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the content of the following URL by issuing a HTTP GET request.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL to retrieve the content for.</param>
|
||||
/// <returns>An object with the result of the request. This object contains the following members:
|
||||
/// - `version`: the protocol of the version.
|
||||
/// - `code`: the HTTP return code.
|
||||
/// - `reason`: the HTTP reason phrase.
|
||||
/// - `headers`: the HTTP returned headers.
|
||||
/// - `content`: the HTTP content. Either a string if the mime type is `text/*` or an object if the mime type is `application/json` otherwise it will return a bytebuffer.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// ```
|
||||
/// >>> wget "https://markdig.azurewebsites.net/"
|
||||
/// # wget("https://markdig.azurewebsites.net/")
|
||||
/// out = {version: "1.1", code: 200, reason: "OK", headers: {"Content-Type": "text/plain; charset=utf-8", "Content-Length": 0}, content: ""}
|
||||
/// ```
|
||||
/// </remarks>
|
||||
[KalkExport("wget", CategoryWeb)]
|
||||
public ScriptObject WebGet(string url)
|
||||
{
|
||||
if (url == null) throw new ArgumentNullException(nameof(url));
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
HttpResponseMessage result;
|
||||
try
|
||||
{
|
||||
var task = Task.Run(async () => await httpClient.GetAsync(url));
|
||||
task.Wait();
|
||||
result = task.Result;
|
||||
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
throw new ArgumentException($"HTTP/{result.Version} {(int)result.StatusCode} {result.ReasonPhrase}", nameof(url));
|
||||
}
|
||||
else
|
||||
{
|
||||
var headers = new ScriptObject();
|
||||
foreach (var headerItem in result.Content.Headers)
|
||||
{
|
||||
var items = new ScriptArray(headerItem.Value);
|
||||
object itemValue = items;
|
||||
if (items.Count == 1)
|
||||
{
|
||||
var str = (string) items[0];
|
||||
itemValue = str;
|
||||
if (str == "true")
|
||||
{
|
||||
itemValue = (KalkBool)true;
|
||||
}
|
||||
else if (str == "false")
|
||||
{
|
||||
itemValue = (KalkBool)false;
|
||||
}
|
||||
else if (long.TryParse(str, out var longValue))
|
||||
{
|
||||
if (longValue >= int.MinValue && longValue <= int.MaxValue)
|
||||
{
|
||||
itemValue = (int) longValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemValue = longValue;
|
||||
}
|
||||
}
|
||||
else if (DateTime.TryParse(str, out var time))
|
||||
{
|
||||
itemValue = time;
|
||||
}
|
||||
}
|
||||
headers[headerItem.Key] = itemValue;
|
||||
}
|
||||
|
||||
var mediaType = result.Content.Headers.ContentType.MediaType;
|
||||
var resultObj = new ScriptObject()
|
||||
{
|
||||
{"version", result.Version.ToString()},
|
||||
{"code", (int) result.StatusCode},
|
||||
{"reason", result.ReasonPhrase},
|
||||
{"headers", headers}
|
||||
};
|
||||
|
||||
if (mediaType.StartsWith("text/") || mediaType == "application/json")
|
||||
{
|
||||
var readTask = Task.Run(async () => await result.Content.ReadAsStringAsync());
|
||||
readTask.Wait();
|
||||
var text = readTask.Result;
|
||||
if (mediaType == "application/json" && TryParseJson(text, out var jsonObject))
|
||||
{
|
||||
resultObj["content"] = jsonObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultObj["content"] = readTask.Result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var readTask = Task.Run(async () => await result.Content.ReadAsByteArrayAsync());
|
||||
readTask.Wait();
|
||||
resultObj["content"] = new KalkNativeBuffer(readTask.Result);
|
||||
}
|
||||
|
||||
return resultObj;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ArgumentException(ex.Message, nameof(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseJson(string text, out ScriptObject scriptObj)
|
||||
{
|
||||
scriptObj = null;
|
||||
try
|
||||
{
|
||||
// Make sure that we can parse the input JSON
|
||||
var jsonDoc = JsonDocument.Parse(text);
|
||||
var result = ConvertFromJson(jsonDoc.RootElement);
|
||||
scriptObj = result as ScriptObject;
|
||||
return scriptObj != null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertToJson(object element, StringBuilder builder)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
builder.Append("null");
|
||||
}
|
||||
else if (element is ScriptObject scriptObject)
|
||||
{
|
||||
builder.Append("{");
|
||||
bool isFirst = true;
|
||||
foreach (var item in scriptObject)
|
||||
{
|
||||
if (!isFirst)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
builder.Append('\"');
|
||||
builder.Append(StringFunctions.Escape(item.Key));
|
||||
builder.Append('\"');
|
||||
builder.Append(": ");
|
||||
ConvertToJson(item.Value, builder);
|
||||
isFirst = false;
|
||||
}
|
||||
builder.Append("}");
|
||||
}
|
||||
else if (element is string text)
|
||||
{
|
||||
builder.Append('\"');
|
||||
builder.Append(StringFunctions.Escape(text));
|
||||
builder.Append('\"');
|
||||
}
|
||||
else if (element.GetType().IsNumber())
|
||||
{
|
||||
builder.Append(Engine.ObjectToString(element));
|
||||
}
|
||||
else if (element is bool rb)
|
||||
{
|
||||
builder.Append(rb ? "true" : "false");
|
||||
}
|
||||
else if (element is KalkBool b)
|
||||
{
|
||||
builder.Append(b ? "true" : "false");
|
||||
}
|
||||
else if (element is IEnumerable it)
|
||||
{
|
||||
builder.Append('[');
|
||||
bool isFirst = true;
|
||||
foreach (var item in it)
|
||||
{
|
||||
if (!isFirst)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
ConvertToJson(item, builder);
|
||||
isFirst = false;
|
||||
}
|
||||
builder.Append(']');
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append('\"');
|
||||
builder.Append(StringFunctions.Escape(Engine.ObjectToString(element)));
|
||||
builder.Append('\"');
|
||||
}
|
||||
}
|
||||
|
||||
private static object ConvertFromJson(JsonElement element)
|
||||
{
|
||||
switch (element.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
var obj = new ScriptObject();
|
||||
foreach (var prop in element.EnumerateObject())
|
||||
{
|
||||
obj[prop.Name] = ConvertFromJson(prop.Value);
|
||||
}
|
||||
|
||||
return obj;
|
||||
case JsonValueKind.Array:
|
||||
var array = new ScriptArray();
|
||||
foreach (var nestedElement in element.EnumerateArray())
|
||||
{
|
||||
array.Add(ConvertFromJson(nestedElement));
|
||||
}
|
||||
return array;
|
||||
case JsonValueKind.String:
|
||||
return element.GetString();
|
||||
case JsonValueKind.Number:
|
||||
if (element.TryGetInt32(out var intValue))
|
||||
{
|
||||
return intValue;
|
||||
}
|
||||
else if (element.TryGetInt64(out var longValue))
|
||||
{
|
||||
return longValue;
|
||||
}
|
||||
else if (element.TryGetUInt32(out var uintValue))
|
||||
{
|
||||
return uintValue;
|
||||
}
|
||||
else if (element.TryGetUInt64(out var ulongValue))
|
||||
{
|
||||
return ulongValue;
|
||||
}
|
||||
else if (element.TryGetDecimal(out var decimalValue))
|
||||
{
|
||||
return decimalValue;
|
||||
}
|
||||
else if (element.TryGetDouble(out var doubleValue))
|
||||
{
|
||||
return doubleValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert number {element}");
|
||||
}
|
||||
case JsonValueKind.True:
|
||||
return (KalkBool)true;
|
||||
case JsonValueKind.False:
|
||||
return (KalkBool)false;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user