process cases of ALT as special rules
This commit is contained in:
+28
-6
@@ -1,19 +1,37 @@
|
||||
<Project>
|
||||
|
||||
<!-- Native builds are opt-in for executable hosts, not repeated by every library project. -->
|
||||
<Target Name="BuildNativeLibrary"
|
||||
BeforeTargets="PrepareForBuild"
|
||||
Condition="'$(BuildNativeBeforeManaged)'=='true' And '$(DesignTimeBuild)'!='true'">
|
||||
<PropertyGroup>
|
||||
<NativeCMakePreset Condition="'$(NativeCMakePreset)'=='' And '$(Configuration)'=='Debug'">clang-debug</NativeCMakePreset>
|
||||
<NativeCMakePreset Condition="'$(NativeCMakePreset)'==''">clang-release</NativeCMakePreset>
|
||||
<NativeCMakeBuildDir Condition="'$(NativeCMakeBuildDir)'==''">$(SolutionRoot)cmake_build/$(NativeCMakePreset)</NativeCMakeBuildDir>
|
||||
<_NativeCMakeOutputDir>$([System.String]::Copy('$(NativeBuildDir)').Replace('\', '/'))</_NativeCMakeOutputDir>
|
||||
</PropertyGroup>
|
||||
<Exec Command="cmake --preset "$(NativeCMakePreset)" -S "$(SolutionRoot)src/Nitload.Native" -B "$(NativeCMakeBuildDir)" "-DNIF_BUILD_OUTPUT_DIR:PATH=$(_NativeCMakeOutputDir)" "-DNIF_EXPECTED_RID:STRING=$(BuildRid)"" />
|
||||
<Exec Command="cmake --build "$(NativeCMakeBuildDir)" --target "$(NativeLibName)"" />
|
||||
<Error Condition="!Exists('$(NativeBuildDir)$(NativeFileName)')"
|
||||
Text="CMake did not produce the expected native library: $(NativeBuildDir)$(NativeFileName). Check the target OS, architecture and output name." />
|
||||
</Target>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<!-- 编译后:把 C# 输出拷贝到 bin/<rid>/ 目录 -->
|
||||
<!-- 替代原来的 xcopy PostBuild -->
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<Target Name="CopyOutputToBuildDir"
|
||||
AfterTargets="Build"
|
||||
Condition="'$(DisablePostBuildCopy)'!='true'">
|
||||
Condition="'$(DisablePostBuildCopy)'!='true' And '$(MSBuildProjectExtension)'=='.csproj' And '$(DesignTimeBuild)'!='true'">
|
||||
|
||||
<MakeDir Directories="$(ManagedBuildDir)" />
|
||||
|
||||
<!-- 拷贝所有编译输出(DLL + PDB + XML + deps.json) -->
|
||||
<ItemGroup>
|
||||
<_OutputFiles Include="$(OutputPath)**\*.*"
|
||||
Exclude="$(OutputPath)**\*.user" />
|
||||
<!-- Native artifacts flow from CMake to hosts only. Never copy an old deployed DLL
|
||||
back over the authoritative CMake output, including pre-rename artifacts. -->
|
||||
<_OutputFiles Include="$(TargetDir)**\*.*"
|
||||
Exclude="$(TargetDir)**\*.user;$(TargetDir)**\$(NativeLibName).*;$(TargetDir)**\lib$(NativeLibName).*;$(TargetDir)**\StgSharp.Native.*;$(TargetDir)**\libStgSharp.Native.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(_OutputFiles)"
|
||||
@@ -30,7 +48,11 @@
|
||||
<!-- ════════════════════════════════════════════════════════ -->
|
||||
<Target Name="CopyNativeToProjectOutput"
|
||||
AfterTargets="Build"
|
||||
Condition="'$(DisableNativeCopy)'!='true'">
|
||||
BeforeTargets="CopyOutputToBuildDir"
|
||||
Condition="'$(DisableNativeCopy)'!='true' And '$(MSBuildProjectExtension)'=='.csproj' And '$(DesignTimeBuild)'!='true'">
|
||||
|
||||
<Error Text="Native library not found at $(NativeBuildDir)$(NativeFileName). Build Nitload.Native with CMake, or enable BuildNativeBeforeManaged."
|
||||
Condition="'$(RequireNativeLibrary)'=='true' And !Exists('$(NativeBuildDir)$(NativeFileName)')" />
|
||||
|
||||
<!-- native 库文件 -->
|
||||
<ItemGroup>
|
||||
@@ -45,12 +67,12 @@
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(_NativeLibFile);@(_NativeSymFile)"
|
||||
DestinationFolder="$(OutputPath)"
|
||||
DestinationFolder="$(TargetDir)"
|
||||
SkipUnchangedFiles="true"
|
||||
Condition="'@(_NativeLibFile)'!='' Or '@(_NativeSymFile)'!=''" />
|
||||
|
||||
<Message Importance="high"
|
||||
Text="[Nitload] Native $(NativeFileName) ($(BuildRid)) → $(OutputPath)"
|
||||
Text="[Nitload] Native $(NativeFileName) ($(BuildRid)) → $(TargetDir)"
|
||||
Condition="'@(_NativeLibFile)'!=''" />
|
||||
|
||||
<Warning Text="[Nitload] Native library not found at $(NativeBuildDir)$(NativeFileName). Build Nitload.Native first!"
|
||||
|
||||
@@ -161,7 +161,7 @@ Nitload is currently in development and requires compilation from source. **NuGe
|
||||
dotnet build Nitload.sln
|
||||
```
|
||||
|
||||
Managed builds copy an existing native binary into their output directory. They do not invoke CMake automatically, so build `Nitload.Native` first when native functionality is required.
|
||||
Managed library builds copy an existing native binary into their output directory. Build `Nitload.Native` first when native functionality is required. The `StgSharpDebug` executable is an exception: its project enables an incremental CMake build before managed compilation and requires the native DLL for the normal runtime path.
|
||||
|
||||
### Build Configuration
|
||||
|
||||
|
||||
@@ -1785,12 +1785,10 @@ namespace Nitload.Graphics.OpenGL
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Uniform(int location, bool transpose, GMatrix44<float> value)
|
||||
{
|
||||
fixed (GMatrix44<float>* __value = &value)
|
||||
{
|
||||
__context->glUniformMatrix4fv(location, 1, transpose ? (byte)1 : (byte)0, (float*)__value);
|
||||
}
|
||||
__context->glUniformMatrix4fv(location, 1, transpose ? (byte)1 : (byte)0, (float*)&value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Forwards to the native OpenGL entry point.</para>
|
||||
/// <para><c>void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);</c></para>
|
||||
|
||||
@@ -633,11 +633,9 @@ namespace Nitload.Graphics.OpenGL
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ProgramUniform(GlHandle program, int location, bool transpose, GMatrix44<float> value)
|
||||
{
|
||||
fixed (GMatrix44<float>* __value = &value)
|
||||
{
|
||||
__context->glProgramUniformMatrix4fv(program.Value, location, 1, transpose ? (byte)1 : (byte)0, (float*)__value);
|
||||
__context->glProgramUniformMatrix4fv(program.Value, location, 1, transpose ? (byte)1 : (byte)0, (float*)&value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Forwards to the native OpenGL entry point.</para>
|
||||
|
||||
@@ -42,10 +42,11 @@ namespace Nitload.Graphics.OpenGL
|
||||
{
|
||||
ContextHandle = Marshal.AllocHGlobal(Marshal.SizeOf<OpenglContext>());
|
||||
OpenglContext* contextID = (OpenglContext*)ContextHandle;
|
||||
contextID->glGetString = (delegate*<uint,byte*>)IntPtr.Zero;
|
||||
contextID->glGetString = (delegate* unmanaged[Cdecl]<uint,byte*>)IntPtr.Zero;
|
||||
}
|
||||
if (GL == null) {
|
||||
_context = OpenGLFunction.BuildGlFunctionPackage(ContextHandle);
|
||||
if (GL == null)
|
||||
{
|
||||
// _context = OpenGLFunction.BuildGlFunctionPackage(ContextHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +61,8 @@ namespace Nitload.Graphics.OpenGL
|
||||
public unsafe void MakeAsCurrentContext()
|
||||
{
|
||||
GraphicFramework.glfwMakeContextCurrent(CanvasHandle);
|
||||
OpenGLFunction.CurrentGL = this.GL;
|
||||
|
||||
// OpenGLFunction.CurrentGL = this.GL;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -73,7 +75,8 @@ namespace Nitload.Graphics.OpenGL
|
||||
SwapBuffers();
|
||||
renderToThread[this] = ThreadHelper.EmptyThread;
|
||||
GraphicFramework.glfwMakeContextCurrent(IntPtr.Zero);
|
||||
OpenGLFunction.CurrentGL = null;
|
||||
|
||||
// OpenGLFunction.CurrentGL = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -121,7 +124,7 @@ namespace Nitload.Graphics.OpenGL
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected sealed override void NativeCameraViewRange(
|
||||
Radius fieldOfRange,
|
||||
Vec2 offset,
|
||||
Vec2<float> offset,
|
||||
(float frontDepth, float backDepth) viewDepth
|
||||
)
|
||||
{
|
||||
@@ -130,9 +133,9 @@ namespace Nitload.Graphics.OpenGL
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected sealed override void NativeCameraViewTarget(
|
||||
Vec3 position,
|
||||
Vec3 direction,
|
||||
Vec3 up
|
||||
Vec3<float> position,
|
||||
Vec3<float> direction,
|
||||
Vec3<float> up
|
||||
)
|
||||
{
|
||||
NativeCamera.SetViewDirection(position, direction, up);
|
||||
|
||||
@@ -16,14 +16,15 @@ namespace Nitload.Graphics
|
||||
public class PlainGeometryMesh
|
||||
{
|
||||
|
||||
private readonly PlainGeometry _shape;
|
||||
private readonly Vec2[] _textureCoordinates;
|
||||
private readonly Vec2<float>[] _textureCoordinates;
|
||||
private readonly Image<Rgba8> _textureImage;
|
||||
|
||||
private readonly PlainGeometry _shape;
|
||||
|
||||
public PlainGeometryMesh(
|
||||
PlainGeometry shape,
|
||||
Image<Rgba8> texture,
|
||||
Vec2[] texCoord
|
||||
Vec2<float>[] texCoord
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(shape);
|
||||
@@ -40,14 +41,13 @@ namespace Nitload.Graphics
|
||||
_textureCoordinates = texCoord;
|
||||
}
|
||||
|
||||
public Vec4[] VertexArray => _shape.VertexStream;
|
||||
public Vec3<float>[] VertexArray => _shape.VertexStream;
|
||||
|
||||
public Image<Rgba8> TextureImage => _textureImage;
|
||||
|
||||
public ReadOnlySpan<int> MeshIndices => _shape.VertexIndices;
|
||||
|
||||
public ReadOnlySpan<Vec4> TextureCoord => MemoryMarshal.Cast<Vec2, Vec4>(
|
||||
_textureCoordinates);
|
||||
public ReadOnlySpan<Vec2<float>> TextureCoord => _textureCoordinates;
|
||||
|
||||
public PlainGeometryMesh Resize(
|
||||
PlainGeometry newShape
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using Nitload.Common.Timing;
|
||||
using Nitload.Mathematics;
|
||||
using Nitload.Mathematics.Numeric.Graphics;
|
||||
using Nitload.Mathematics.Numeric.Graphics;
|
||||
using Nitload.Common.Timing;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -123,14 +123,14 @@ namespace Nitload.Graphics
|
||||
|
||||
protected abstract void NativeCameraViewRange(
|
||||
Radius fieldOfRange,
|
||||
Vec2 offset,
|
||||
Vec2<float> offset,
|
||||
(float frontDepth, float backDepth) viewDepth
|
||||
);
|
||||
|
||||
protected abstract void NativeCameraViewTarget(
|
||||
Vec3 position,
|
||||
Vec3 direction,
|
||||
Vec3 up
|
||||
Vec3<float> position,
|
||||
Vec3<float> direction,
|
||||
Vec3<float> up
|
||||
);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -24,12 +24,11 @@ namespace Nitload.Mathematics.Numeric.Graphics
|
||||
|
||||
private Buffer _buffer;
|
||||
|
||||
public Vec4<T> this[
|
||||
int x
|
||||
internal unsafe ref Vec4<T> this[
|
||||
int x
|
||||
]
|
||||
{
|
||||
readonly get => _buffer[x];
|
||||
set => _buffer[x] = value;
|
||||
get => ref Unsafe.Add(ref Unsafe.As<GMatrix44<T>, Vec4<T>>(ref this), x);
|
||||
}
|
||||
|
||||
public T this[
|
||||
@@ -178,6 +177,75 @@ namespace Nitload.Mathematics.Numeric.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GMatrix44<T> operator -(
|
||||
GMatrix44<T> left,
|
||||
GMatrix44<T> right
|
||||
)
|
||||
{
|
||||
Unsafe.SkipInit(out GMatrix44<T> result);
|
||||
result._buffer[0] = left._buffer[0] - right._buffer[0];
|
||||
result._buffer[1] = left._buffer[1] - right._buffer[1];
|
||||
result._buffer[2] = left._buffer[2] - right._buffer[2];
|
||||
result._buffer[3] = left._buffer[3] - right._buffer[3];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GMatrix44<T> operator *(
|
||||
GMatrix44<T> left,
|
||||
T right
|
||||
)
|
||||
{
|
||||
Unsafe.SkipInit(out GMatrix44<T> result);
|
||||
result._buffer[0] = left._buffer[0] * right;
|
||||
result._buffer[1] = left._buffer[1] * right;
|
||||
result._buffer[2] = left._buffer[2] * right;
|
||||
result._buffer[3] = left._buffer[3] * right;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GMatrix44<T> operator *(
|
||||
T left,
|
||||
GMatrix44<T> right
|
||||
)
|
||||
{
|
||||
return right * left;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GMatrix44<T> operator *(
|
||||
GMatrix44<T> left,
|
||||
GMatrix44<T> right
|
||||
)
|
||||
{
|
||||
Unsafe.SkipInit(out GMatrix44<T> result);
|
||||
result._buffer[0] = left._buffer[0] + right._buffer[0];
|
||||
result._buffer[1] = left._buffer[1] + right._buffer[1];
|
||||
result._buffer[2] = left._buffer[2] + right._buffer[2];
|
||||
result._buffer[3] = left._buffer[3] + right._buffer[3];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GMatrix44<T> operator +(
|
||||
GMatrix44<T> left,
|
||||
GMatrix44<T> right
|
||||
)
|
||||
{
|
||||
Unsafe.SkipInit(out GMatrix44<T> result);
|
||||
result._buffer[0] = left._buffer[0] + right._buffer[0];
|
||||
result._buffer[1] = left._buffer[1] + right._buffer[1];
|
||||
result._buffer[2] = left._buffer[2] + right._buffer[2];
|
||||
result._buffer[3] = left._buffer[3] + right._buffer[3];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[InlineArray(4)]
|
||||
private struct Buffer
|
||||
{
|
||||
|
||||
@@ -70,7 +70,13 @@ else()
|
||||
set(NIF_ARCH_TAG ${NIF_RESOLVED_ARCH_NAME})
|
||||
endif()
|
||||
|
||||
set(NIF_BUILD_OUTPUT_DIR ${NIF_SOLUTION_ROOT}/bin/${NIF_OS_TAG}-${NIF_ARCH_TAG})
|
||||
set(NIF_EXPECTED_RID "" CACHE STRING "Optional managed target RID to validate before deployment")
|
||||
if(NIF_EXPECTED_RID AND NOT NIF_EXPECTED_RID STREQUAL "${NIF_OS_TAG}-${NIF_ARCH_TAG}")
|
||||
message(FATAL_ERROR "Native target ${NIF_OS_TAG}-${NIF_ARCH_TAG} does not match managed target ${NIF_EXPECTED_RID}. Use a matching compiler/toolchain and a separate build directory.")
|
||||
endif()
|
||||
|
||||
set(NIF_BUILD_OUTPUT_DIR "${NIF_SOLUTION_ROOT}/bin/${NIF_OS_TAG}-${NIF_ARCH_TAG}"
|
||||
CACHE PATH "Native artifact directory shared with managed build deployment")
|
||||
# Compile-time intermediates (native PDB / .obj side files) live inside the CMake
|
||||
# build tree (cmake_build/<preset>) so they get cleaned up with it.
|
||||
set(NIF_OBJECT_ROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/obj/${PROJECT_NAME})
|
||||
@@ -86,9 +92,21 @@ source_group(TREE ${CMAKE_CURRENT_LIST_DIR} FILES ${NIF_NATIVE_PROJECT_FILES})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
C_STANDARD 11
|
||||
C_STANDARD_REQUIRED ON
|
||||
RUNTIME_OUTPUT_DIRECTORY ${NIF_BUILD_OUTPUT_DIR}
|
||||
LIBRARY_OUTPUT_DIRECTORY ${NIF_BUILD_OUTPUT_DIR}
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${NIF_BUILD_OUTPUT_DIR})
|
||||
OUTPUT_NAME "Nitload.Native"
|
||||
PDB_NAME "Nitload.Native"
|
||||
DEBUG_POSTFIX ""
|
||||
RELEASE_POSTFIX ""
|
||||
RELWITHDEBINFO_POSTFIX ""
|
||||
MINSIZEREL_POSTFIX ""
|
||||
RUNTIME_OUTPUT_DIRECTORY "${NIF_BUILD_OUTPUT_DIR}"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${NIF_BUILD_OUTPUT_DIR}"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${NIF_BUILD_OUTPUT_DIR}"
|
||||
PDB_OUTPUT_DIRECTORY "${NIF_BUILD_OUTPUT_DIR}")
|
||||
|
||||
# P/Invoke loads Nitload.Native.dll on Windows, including GNU-style Clang toolchains.
|
||||
if(WIN32)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
||||
endif()
|
||||
|
||||
foreach(_config Debug Release RelWithDebInfo MinSizeRel)
|
||||
string(TOUPPER ${_config} _config_upper)
|
||||
|
||||
@@ -85,9 +85,10 @@
|
||||
<!-- 把 ARM64 → arm64,x64 保持 x64 -->
|
||||
<_ArchTag Condition="'$(Platform)'=='x64'">x64</_ArchTag>
|
||||
<_ArchTag Condition="'$(Platform)'=='ARM64'">arm64</_ArchTag>
|
||||
<!-- 输出到 cmake_build/win_<arch>/,与 CMake 目录约定保持一致 -->
|
||||
<OutDir>$(SolutionDir)cmake_build\win_$(_ArchTag)\</OutDir>
|
||||
<IntDir>$(SolutionDir)obj\$(ProjectName)\$(Configuration)\$(Platform)\</IntDir>
|
||||
<!-- Keep the navigation project's artifact paths aligned with CMake and managed deployment. -->
|
||||
<TargetName>Nitload.Native</TargetName>
|
||||
<OutDir>$(NativeBuildDir)</OutDir>
|
||||
<IntDir>$(SolutionRoot)obj\$(ProjectName)\$(Configuration)\$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
|
||||
@@ -6,6 +6,32 @@ Though this project do has a .vcxproj file,
|
||||
you can only edit but not compile in visul studio.
|
||||
Currently you can only compile this lib via Clang and cmake.
|
||||
|
||||
## Build output and managed deployment
|
||||
|
||||
From the repository root:
|
||||
|
||||
```powershell
|
||||
cmake --preset clang-debug -S src/Nitload.Native
|
||||
cmake --build cmake_build/clang-debug --target Nitload.Native
|
||||
```
|
||||
|
||||
CMake writes `Nitload.Native.dll` and its linker PDB to `bin/win-x64` on Windows x64
|
||||
(Linux uses `bin/linux-x64/libNitload.Native.so`). Debug and release presets currently
|
||||
share this platform directory; the most recent native build is the deployed version.
|
||||
`NIF_BUILD_OUTPUT_DIR` can override the destination. Managed builds pass their expected
|
||||
RID as `NIF_EXPECTED_RID` so an incompatible native target is rejected before deployment.
|
||||
|
||||
`StgSharpDebug.csproj` enables `BuildNativeBeforeManaged` and `RequireNativeLibrary`.
|
||||
Its build runs the incremental CMake build (`clang-debug` for Debug, `clang-release`
|
||||
otherwise), then deploys the native library to the executable's actual `TargetDir`.
|
||||
Other managed projects copy an existing native artifact without invoking CMake.
|
||||
Managed-output staging never copies deployed native files back over the CMake artifact.
|
||||
|
||||
To use a prebuilt native artifact, pass `-p:BuildNativeBeforeManaged=false`. A matching
|
||||
Clang/Ninja environment and the local GLFW static library are required for native builds.
|
||||
Cross-target builds require a matching toolchain/preset; changing a managed platform alone
|
||||
does not cross-compile the C library.
|
||||
|
||||
## Code Layout
|
||||
|
||||
Conventions:
|
||||
|
||||
@@ -39,13 +39,13 @@ namespace Nitload.RegularAnalysis.Text
|
||||
GenerateConcat(root, sc, func_list, context, exit_label);
|
||||
break;
|
||||
case RegexElementLabel.COUNT:
|
||||
GenerateCount(root, sc, func_list, context);
|
||||
GenerateCount(root, sc, func_list, context, exit_label);
|
||||
break;
|
||||
case RegexElementLabel.GROUP_BEGIN:
|
||||
GenerateGroup(root, sc, func_list, context);
|
||||
GenerateGroup(root, sc, func_list, context, exit_label);
|
||||
break;
|
||||
case RegexElementLabel.ALT:
|
||||
GenerateAlt(root, sc, func_list, context);
|
||||
GenerateAlt(root, sc, func_list, context, exit_label);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -167,17 +167,20 @@ namespace Nitload.RegularAnalysis.Text
|
||||
)
|
||||
{
|
||||
RegexCharSetPayload payload = value.PayloadAs<RegexCharSetPayload>();
|
||||
string[] rules = new string[payload.Set.Count];
|
||||
for (int i = 0; i < rules.Length; i++) {
|
||||
rules[i] = GenerateCharSetExpression(payload.Set[i]);
|
||||
}
|
||||
|
||||
// Combine rule-level complements before applying the enclosing charset complement.
|
||||
// An empty union accepts nothing; its complement accepts any available character.
|
||||
string predicate = rules.Length == 0 ? "false" : string.Join(" || ", rules);
|
||||
string mismatch = payload.Accept ? $"!({predicate})" : $"({predicate})";
|
||||
string remain_span = context.RemainSpan;
|
||||
_ = sc.AppendLine($"if ({remain_span}.IsEmpty || {mismatch})")
|
||||
_ = sc.AppendLine($"if ({remain_span}.IsEmpty ||")
|
||||
.AppendLine(payload.Accept ? " !(" : " (");
|
||||
if (payload.Set.Count == 0) {
|
||||
_ = sc.AppendLine(" false");
|
||||
}
|
||||
for (int i = 0; i < payload.Set.Count; i++)
|
||||
{
|
||||
string suffix = i + 1 < payload.Set.Count ? " ||" : string.Empty;
|
||||
_ = sc.AppendLine($" {GenerateCharSetExpression(payload.Set[i])}{suffix}");
|
||||
}
|
||||
_ = sc.AppendLine(" ))")
|
||||
.AppendLine("{")
|
||||
.AppendLine($"goto {exit.GetLabelName()};")
|
||||
.AppendLine("}")
|
||||
@@ -244,9 +247,6 @@ namespace Nitload.RegularAnalysis.Text
|
||||
string required_chunk_index = $"__required_chunk_index_{scratch_scope_index}";
|
||||
string additional_count = $"__additional_count_{scratch_scope_index}";
|
||||
|
||||
// TODO(NGRA): Replace this local COUNT exit with the inherited failure ExitLabel.
|
||||
// The temporary goto preserves the former local exit but still lets siblings execute.
|
||||
|
||||
if (min <= flattened_unit_count_threshold)
|
||||
{
|
||||
if (min > 0)
|
||||
@@ -428,9 +428,6 @@ namespace Nitload.RegularAnalysis.Text
|
||||
string additional_count = $"__additional_count_{scratch_scope_index}";
|
||||
string starts_with = $"{nameof(MemoryExtensions.StartsWith)}";
|
||||
|
||||
// TODO(NGRA): Replace this local COUNT exit with the inherited failure ExitLabel.
|
||||
// The temporary goto preserves the former local exit but still lets siblings execute.
|
||||
|
||||
if (literal.Length > flattened_unit_count_threshold)
|
||||
{
|
||||
// One repetition is already larger than the flattening threshold. Match the
|
||||
@@ -551,93 +548,28 @@ namespace Nitload.RegularAnalysis.Text
|
||||
RegexAstNode node,
|
||||
[NotNull] in SequenceEmitter<string> sc,
|
||||
in List<SequenceEmitter<string>> func_list,
|
||||
in SourceGenContext context
|
||||
in SourceGenContext context,
|
||||
in ExitLabel exit_label
|
||||
)
|
||||
{
|
||||
ExitLabel success = context.RequestExitLabel(ExitMode.Goto);
|
||||
RegexAstNode current = node.Right;
|
||||
|
||||
// TODO(NGRA-NAMING): Reserve exclusive names for future ALT cursor/capture snapshots;
|
||||
// their lifetime extends through retry labels, not just the candidate's emitted block.
|
||||
ExitLabel exit_label = context.RequestExitLabel(ExitMode.Goto);
|
||||
|
||||
// TODO(NGRA): Walk every normalized ALT candidate exactly once. The final traversal
|
||||
// shape and failure continuation are not wired yet.
|
||||
while (RegexAstNode.IsNullOrEmpty(current))
|
||||
while (!RegexAstNode.IsNullOrEmpty(current))
|
||||
{
|
||||
RegexElementLabel op = current.Label & RegexElementLabel.SEQUENCE;
|
||||
if (op != 0)
|
||||
{
|
||||
string code = current.SourceCode;
|
||||
switch (op)
|
||||
{
|
||||
case RegexElementLabel.UNIT:
|
||||
|
||||
// TODO(NGRA): Guard against an empty remaining span before indexing it.
|
||||
_ = sc.AppendLine(@$"if({context.RemainSpan}[0] == {FormatCharLiteral(code[0])})")
|
||||
.AppendLine("{")
|
||||
.AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);")
|
||||
.AppendLine($@"goto {exit_label};")
|
||||
.AppendLine("}");
|
||||
break;
|
||||
case RegexElementLabel.UNIT_SET:
|
||||
|
||||
// TODO(NGRA): Guard the remaining span, handle empty/Any sets, and
|
||||
// validate negated-set composition before emitting this predicate.
|
||||
RegexCharSetPayload payload = current.PayloadAs<RegexCharSetPayload>();
|
||||
IReadOnlyList<RegexCharSet> split = payload.Set;
|
||||
bool accept = payload.Accept;
|
||||
RegexCharSet _set = split[0];
|
||||
_ = sc.AppendLine($"if({(accept ? string.Empty : "!(")} {GenerateCharSetExpression(_set)} ||");
|
||||
for (int i = 1; i < split.Count - 1; i++)
|
||||
{
|
||||
_set = split[i];
|
||||
_ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ||");
|
||||
}
|
||||
_set = split[^1];
|
||||
_ = sc.AppendLine($" {GenerateCharSetExpression(_set)} ){(accept ? string.Empty : ')')}")
|
||||
.AppendLine("{")
|
||||
.AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice(1);")
|
||||
.AppendLine($@"goto {exit_label};")
|
||||
.AppendLine("}");
|
||||
break;
|
||||
case RegexElementLabel.UNIT_SPAN:
|
||||
|
||||
// TODO(NGRA): Advance by code.Length rather than one character and
|
||||
// preserve the ALT rollback cursor on failure.
|
||||
string literal = FormatStringLiteral(code);
|
||||
_ = sc.AppendLine(@$"if({context.RemainSpan}.{MemoryExtensions.StartsWith}({literal}))")
|
||||
.AppendLine("{")
|
||||
.AppendLine($@"{context.RemainSpan} = {context.RemainSpan}.Slice({code.Length});")
|
||||
.AppendLine($@"goto {exit_label};")
|
||||
.AppendLine("}");
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
// TODO(NGRA): Route unsupported ALT candidates through the recursive
|
||||
// matcher or report a source-generation diagnostic.
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(NGRA): Advance to the next ALT candidate after a scalar candidate
|
||||
// misses; the current sequence branch does not update current.
|
||||
} else
|
||||
{
|
||||
GenerateMethodSource(current, sc, func_list, context);
|
||||
current = current.Next;
|
||||
}
|
||||
GenerateAltCandidate(current, sc, func_list, context, success);
|
||||
current = current.Next;
|
||||
}
|
||||
|
||||
// TODO(NGRA): Emit the all-alternatives-failed continuation before the shared success
|
||||
// label, including restoration of the entry cursor and capture state.
|
||||
_ = sc.AppendLine($@"{exit_label}: {{ }}");
|
||||
_ = sc.AppendLine($"goto {exit_label.GetLabelName()};")
|
||||
.AppendLine($"{success.GetLabelName()}: ;");
|
||||
}
|
||||
|
||||
private static void GenerateGroup(
|
||||
RegexAstNode node,
|
||||
[NotNull] in SequenceEmitter<string> sc,
|
||||
in List<SequenceEmitter<string>> func_list,
|
||||
in SourceGenContext context
|
||||
in SourceGenContext context,
|
||||
in ExitLabel exit_label
|
||||
)
|
||||
{
|
||||
RegexAstNode left = node.Left;
|
||||
@@ -648,14 +580,13 @@ namespace Nitload.RegularAnalysis.Text
|
||||
// TODO(NGRA-NAMING): Reserve exclusive names for generated group snapshots and capture
|
||||
// temporaries. A capture's user-provided name is not a unique generated local name.
|
||||
|
||||
// TODO(NGRA): Select the owned group body using Empty-sentinel checks, snapshot its
|
||||
// start cursor, and restore it when the child matcher fails.
|
||||
if (left is not null)
|
||||
// TODO(NGRA): Snapshot the group start cursor and restore it when the child fails.
|
||||
if (!RegexAstNode.IsNullOrEmpty(left))
|
||||
{
|
||||
GenerateMethodSource(left, sc, func_list, context);
|
||||
GenerateMethodSource(left, sc, func_list, context, exit_label);
|
||||
} else
|
||||
if (right is not null) {
|
||||
GenerateMethodSource(right, sc, func_list, context);
|
||||
if (!RegexAstNode.IsNullOrEmpty(right)) {
|
||||
GenerateMethodSource(right, sc, func_list, context, exit_label);
|
||||
}
|
||||
|
||||
string group_name = config.Name;
|
||||
@@ -685,7 +616,8 @@ namespace Nitload.RegularAnalysis.Text
|
||||
RegexAstNode node,
|
||||
[NotNull] in SequenceEmitter<string> sc,
|
||||
in List<SequenceEmitter<string>> func_list,
|
||||
in SourceGenContext context
|
||||
in SourceGenContext context,
|
||||
in ExitLabel exit_label
|
||||
)
|
||||
{
|
||||
RegexAstNode value = RegexAstNode.IsNullOrEmpty(node.Left) ? node.Right : node.Left;
|
||||
@@ -699,12 +631,11 @@ namespace Nitload.RegularAnalysis.Text
|
||||
|
||||
// TODO(NGRA-NAMING): Reserve exclusive names for future repetition/rollback state
|
||||
// across complex COUNT retries; recursive children must use the same method pool.
|
||||
ExitLabel exit_label = context.RequestExitLabel(ExitMode.Goto);
|
||||
if ((value.Label & RegexElementLabel.SEQUENCE) == 0)
|
||||
{
|
||||
// TODO(NGRA): Emit min/max repetition, zero-width progress protection, greedy vs.
|
||||
// lazy ordering, and per-attempt rollback for a complex COUNT body.
|
||||
GenerateMethodSource(value, sc, func_list, context);
|
||||
GenerateMethodSource(value, sc, func_list, context, exit_label);
|
||||
} else
|
||||
{
|
||||
switch (value.Label & RegexElementLabel.SEQUENCE)
|
||||
@@ -726,9 +657,6 @@ namespace Nitload.RegularAnalysis.Text
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(NGRA): Connect COUNT success/failure continuations to the surrounding rollback
|
||||
// scope; the label is currently only a placeholder.
|
||||
_ = sc.AppendLine($@"{exit_label}: {{ }}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="TextRegexSourceGen.GenSpecial"
|
||||
// Project: Nitload
|
||||
// Project: StgSharp
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -24,11 +24,162 @@ namespace Nitload.RegularAnalysis.Text
|
||||
// to the general path; returning true prevents that subtree from being emitted twice.
|
||||
return node.Label switch
|
||||
{
|
||||
RegexElementLabel.CONCAT => TryGenerateFindConcat(node, sc, func_list, context, exit_label),
|
||||
RegexElementLabel.CONCAT => TryGenerateFindConcat(node, sc, func_list, context,
|
||||
exit_label),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
#region ALT candidate rules
|
||||
|
||||
private static bool TryGetSimpleAltNode(
|
||||
RegexAstNode node,
|
||||
out RegexAstNode simple
|
||||
)
|
||||
{
|
||||
simple = node;
|
||||
while (simple.Label == RegexElementLabel.CONCAT)
|
||||
{
|
||||
bool left_empty = RegexAstNode.IsNullOrEmpty(simple.Left);
|
||||
bool right_empty = RegexAstNode.IsNullOrEmpty(simple.Right);
|
||||
if (left_empty == right_empty) {
|
||||
return false;
|
||||
}
|
||||
simple = left_empty ? simple.Right : simple.Left;
|
||||
}
|
||||
|
||||
// Inspect transparent wrappers without changing ownership or the candidate Next link.
|
||||
// COUNT and GROUP are never peeled: repetition and capture semantics must survive.
|
||||
return simple.Label is RegexElementLabel.UNIT or RegexElementLabel.UNIT_SET or
|
||||
RegexElementLabel.UNIT_SPAN;
|
||||
}
|
||||
|
||||
private static void GenerateAltCandidate(
|
||||
RegexAstNode node,
|
||||
in SequenceEmitter<string> sc,
|
||||
in List<SequenceEmitter<string>> func_list,
|
||||
in SourceGenContext context,
|
||||
in ExitLabel success
|
||||
)
|
||||
{
|
||||
if (TryGetSimpleAltNode(node, out RegexAstNode simple)) {
|
||||
node = simple;
|
||||
}
|
||||
|
||||
switch (node.Label)
|
||||
{
|
||||
case RegexElementLabel.UNIT:
|
||||
GenerateAltUnit(node, sc, context, success);
|
||||
break;
|
||||
case RegexElementLabel.UNIT_SET:
|
||||
GenerateAltUnitSet(node, sc, context, success);
|
||||
break;
|
||||
case RegexElementLabel.UNIT_SPAN:
|
||||
GenerateAltUnitSpan(node, sc, context, success);
|
||||
break;
|
||||
case RegexElementLabel.CONCAT when RegexAstNode.IsNullOrEmpty(node.Left) &&
|
||||
RegexAstNode.IsNullOrEmpty(node.Right):
|
||||
case RegexElementLabel.EPSILON:
|
||||
|
||||
// An empty expression succeeds without consuming input.
|
||||
_ = sc.AppendLine($"goto {success.GetLabelName()};");
|
||||
break;
|
||||
case RegexElementLabel.CONCAT:
|
||||
case RegexElementLabel.COUNT:
|
||||
case RegexElementLabel.GROUP_BEGIN:
|
||||
case RegexElementLabel.ALT:
|
||||
GenerateAltOperator(node, sc, func_list, context, success);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"Unsupported ALT candidate: {node.Label}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateAltUnit(
|
||||
RegexAstNode node,
|
||||
in SequenceEmitter<string> sc,
|
||||
in SourceGenContext context,
|
||||
in ExitLabel success
|
||||
)
|
||||
{
|
||||
string literal = node.Value;
|
||||
if (literal.Length != 1) {
|
||||
throw new InvalidOperationException("ALT UNIT requires one decoded character.");
|
||||
}
|
||||
|
||||
_ = sc.AppendLine($"if (!{context.RemainSpan}.IsEmpty && {context.RemainSpan}[0] == {FormatCharLiteral(literal[0])})")
|
||||
.AppendLine("{")
|
||||
.AppendLine($"{context.RemainSpan} = {context.RemainSpan}.Slice(1);")
|
||||
.AppendLine($"goto {success.GetLabelName()};")
|
||||
.AppendLine("}");
|
||||
}
|
||||
|
||||
private static void GenerateAltUnitSet(
|
||||
RegexAstNode node,
|
||||
in SequenceEmitter<string> sc,
|
||||
in SourceGenContext context,
|
||||
in ExitLabel success
|
||||
)
|
||||
{
|
||||
RegexCharSetPayload payload = node.PayloadAs<RegexCharSetPayload>();
|
||||
_ = sc.AppendLine($"if (!{context.RemainSpan}.IsEmpty &&")
|
||||
.AppendLine(payload.Accept ? " (" : " !(");
|
||||
if (payload.Set.Count == 0) {
|
||||
_ = sc.AppendLine(" false");
|
||||
}
|
||||
for (int i = 0; i < payload.Set.Count; i++)
|
||||
{
|
||||
string suffix = i + 1 < payload.Set.Count ? " ||" : string.Empty;
|
||||
_ = sc.AppendLine($" {GenerateCharSetExpression(payload.Set[i])}{suffix}");
|
||||
}
|
||||
_ = sc.AppendLine(" ))")
|
||||
.AppendLine("{")
|
||||
.AppendLine($"{context.RemainSpan} = {context.RemainSpan}.Slice(1);")
|
||||
.AppendLine($"goto {success.GetLabelName()};")
|
||||
.AppendLine("}");
|
||||
}
|
||||
|
||||
private static void GenerateAltUnitSpan(
|
||||
RegexAstNode node,
|
||||
in SequenceEmitter<string> sc,
|
||||
in SourceGenContext context,
|
||||
in ExitLabel success
|
||||
)
|
||||
{
|
||||
string literal = node.Value;
|
||||
if (literal.Length == 0) {
|
||||
throw new InvalidOperationException("ALT UNIT_SPAN requires a non-empty literal.");
|
||||
}
|
||||
string predicate = $"{context.RemainSpan}.{nameof(MemoryExtensions.StartsWith)}({FormatStringLiteral(literal)})";
|
||||
_ = sc.AppendLine($"if ({predicate})")
|
||||
.AppendLine("{")
|
||||
.AppendLine($"{context.RemainSpan} = {context.RemainSpan}.Slice({literal.Length});")
|
||||
.AppendLine($"goto {success.GetLabelName()};")
|
||||
.AppendLine("}");
|
||||
}
|
||||
|
||||
private static void GenerateAltOperator(
|
||||
RegexAstNode node,
|
||||
in SequenceEmitter<string> sc,
|
||||
in List<SequenceEmitter<string>> func_list,
|
||||
in SourceGenContext context,
|
||||
in ExitLabel success
|
||||
)
|
||||
{
|
||||
ExitLabel failure = context.RequestExitLabel(ExitMode.Goto);
|
||||
string entry = $"__alt_entry_{context.RequestScratchScopeIndex()}";
|
||||
_ = sc.AppendLine($"{ROS_char} {entry} = {context.RemainSpan};");
|
||||
GenerateMethodSource(node, sc, func_list, context, failure);
|
||||
_ = sc.AppendLine($"goto {success.GetLabelName()};")
|
||||
.AppendLine($"{failure.GetLabelName()}:")
|
||||
.AppendLine($"{context.RemainSpan} = {entry};");
|
||||
|
||||
// TODO(NGRA): Restore capture state once GROUP emission supports captures.
|
||||
// Retrying an already successful candidate after a later continuation fails is separate.
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FIND concatenation
|
||||
|
||||
private static bool TryGenerateFindConcat(
|
||||
@@ -78,7 +229,8 @@ namespace Nitload.RegularAnalysis.Text
|
||||
{
|
||||
// (?<name>.*)abc
|
||||
|
||||
if (RegexAstNode.IsNullOrEmpty(node) || node.Label != RegexElementLabel.CONCAT) {
|
||||
if (RegexAstNode.IsNullOrEmpty(node) || node.Label != RegexElementLabel.CONCAT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
RegexAstNode group = node.Left;
|
||||
@@ -106,6 +258,5 @@ namespace Nitload.RegularAnalysis.Text
|
||||
// An escaped star is a literal and must not be classified as dot-star repetition.
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,4 +28,4 @@ cmake --build cmake_build/clang-release
|
||||
dotnet build Nitload.sln
|
||||
```
|
||||
|
||||
Managed builds copy an existing native binary when one is available; they do not invoke CMake automatically. Generated OpenGL files under `Nitload.Graphics/OpenGL/Generated` should be updated through the generator in `tools/Nitload.GenerateGL`, not edited by hand.
|
||||
Managed library builds copy an existing native binary when one is available; they do not invoke CMake automatically. The `test/StgSharpDebug` executable explicitly enables the CMake build before managed compilation. Generated OpenGL files under `Nitload.Graphics/OpenGL/Generated` should be updated through the generator in `tools/Nitload.GenerateGL`, not edited by hand.
|
||||
|
||||
@@ -16,4 +16,36 @@ Run the active debug harness directly when investigating a subsystem:
|
||||
dotnet run --project test/StgSharpDebug/StgSharpDebug.csproj
|
||||
```
|
||||
|
||||
## NGRA generation preview
|
||||
|
||||
`StgSharpDebug/TestRegexGenerate.cs` contains a standalone debugging routine that calls the
|
||||
existing NGRA emitter without Roslyn attribute binding. Select it from the debug `Main`:
|
||||
|
||||
```powershell
|
||||
dotnet run --project test/StgSharpDebug/StgSharpDebug.csproj -- --regex-preview
|
||||
```
|
||||
|
||||
This branch returns before `World` or native-memory initialization. Without the switch, the
|
||||
existing debug flow is unchanged. You can also call `TestRegexGenerate.Run()` directly from
|
||||
`Main` while debugging; pass a pattern to `Run("abc")` for a single preview.
|
||||
The switch only selects runtime behavior; building the debug project still builds its normal
|
||||
project references, including Graphics and Mathematics, and prepares the native library with
|
||||
CMake. `BuildNativeBeforeManaged` defaults to true; Debug selects `clang-debug`, and other
|
||||
configurations select `clang-release`. `RequireNativeLibrary` reports a missing DLL at build
|
||||
time instead of allowing the default runtime path to fail later.
|
||||
|
||||
For managed-only preview work without a native toolchain or DLL, add
|
||||
`-p:BuildNativeBeforeManaged=false -p:RequireNativeLibrary=false` before `--` in the run command.
|
||||
|
||||
Enter raw regular expressions without C# string delimiters. The routine prints only the emitted
|
||||
code, without status headers or file output. Exceptions propagate directly to the caller/debugger.
|
||||
Use `:exit` or end-of-input to leave the loop. Empty input is an empty regex. For one-shot mode:
|
||||
|
||||
```powershell
|
||||
dotnet run --project test/StgSharpDebug/StgSharpDebug.csproj -- --regex-preview 'a{2,3}'
|
||||
```
|
||||
|
||||
Set breakpoints in `TestRegexGenerate.cs` or any existing `Generate*` method. The output is a
|
||||
method-body fragment; this routine does not compile or execute it.
|
||||
|
||||
New repeatable tests should use a test project and avoid machine-specific paths, external mutable data, and dependencies on previous test execution. Keep one-off investigations in `SingleFile` or the manual debug harness until their expected behavior is stable enough for automation.
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using Nitload.Graphics;
|
||||
using Nitload.Common.HighPerformance.Memory;
|
||||
using Nitload.Common.Internal;
|
||||
using Nitload.Common.Timing;
|
||||
using Nitload.Common.Timing;
|
||||
using Nitload.Graphics;
|
||||
using Nitload.Mathematics;
|
||||
using Nitload.Mathematics.Numeric;
|
||||
using Nitload.Common.Timing;
|
||||
using Nitload.Common.Timing;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -28,6 +28,13 @@ namespace Nitload
|
||||
string[] args
|
||||
)
|
||||
{
|
||||
// Select the managed-only emitter preview before initializing the normal runtime.
|
||||
if (args.Length > 0 && args[0] == "--regex-preview")
|
||||
{
|
||||
Environment.ExitCode = TestRegexGenerate.Run(args[1..]);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
World.Initialize(new()
|
||||
@@ -39,13 +46,8 @@ namespace Nitload
|
||||
// .UseModule<GraphicModule>()
|
||||
.InitializeModule();
|
||||
|
||||
_ = SlabAllocator.FromBuffer<int>(
|
||||
(nuint)NativeMemory.AlignedAlloc(65536 * 4, 4),
|
||||
65536,
|
||||
buffer => NativeMemory.AlignedFree((void*)buffer)
|
||||
);
|
||||
|
||||
TestRegexAnalyse.Run();
|
||||
// TestRegexAnalyse.Run();
|
||||
_ = TestRegexGenerate.Run("abc|123");
|
||||
|
||||
// TestHlsf();
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<BuildNativeBeforeManaged>true</BuildNativeBeforeManaged>
|
||||
<RequireNativeLibrary>true</RequireNativeLibrary>
|
||||
<!-- Let MSBuild run the incremental CMake build even when managed inputs are unchanged. -->
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -9,6 +13,11 @@
|
||||
<None Remove="SSG_errorlog.log" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- The debug host calls the emitter directly and needs Roslyn at runtime. -->
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Nitload.Common\Nitload.Common.csproj" />
|
||||
<ProjectReference Include="..\..\src\Nitload.Graphics\Nitload.Graphics.csproj" />
|
||||
@@ -18,4 +27,3 @@
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="TestRegexGenerate"
|
||||
// Project: Nitload
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using Nitload.RegularAnalysis.Abstraction;
|
||||
using Nitload.RegularAnalysis.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using static Nitload.RegularAnalysis.Text.TextRegexSourceGen;
|
||||
|
||||
namespace Nitload
|
||||
{
|
||||
internal static class TestRegexGenerate
|
||||
{
|
||||
public static int Run(params string[] args)
|
||||
{
|
||||
if (args.Length > 1) {
|
||||
throw new ArgumentException("Expected at most one regex pattern.", nameof(args));
|
||||
}
|
||||
if (args.Length == 1)
|
||||
{
|
||||
Print(args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.Write("regex> ");
|
||||
string? pattern = Console.ReadLine();
|
||||
if (pattern is null or ":exit") {
|
||||
return 0;
|
||||
}
|
||||
Print(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Print(string pattern)
|
||||
{
|
||||
TextRegexSource analysis = RegexAnalyzer.Analyze(pattern);
|
||||
if (analysis.AnalyseException is not null) {
|
||||
ExceptionDispatchInfo.Throw(analysis.AnalyseException);
|
||||
}
|
||||
|
||||
SequenceEmitter<string> body = new();
|
||||
List<SequenceEmitter<string>> functions = [];
|
||||
SourceGenContext context = new();
|
||||
ExitLabel exit = context.RequestExitLabel(ExitMode.Goto);
|
||||
GenerateMethodSource(analysis.Ast.Root, body, functions, context, exit);
|
||||
|
||||
foreach (string line in body.ToList()) {
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
foreach (SequenceEmitter<string> function in functions)
|
||||
{
|
||||
foreach (string line in function.ToList()) {
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user