process cases of ALT as special rules
This commit is contained in:
@@ -1785,11 +1785,9 @@ 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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user