Browse Source

Merge remote-tracking branch 'origin/feature-debug' into feature-debug

Akeit0 8 months ago
parent
commit
7a0bd03a3e

+ 234 - 0
.editorconfig

@@ -0,0 +1,234 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_collection_expression = when_types_loosely_match
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Field preferences
+dotnet_style_readonly_field = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:silent
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = true
+dotnet_style_allow_statement_immediately_after_block_experimental = true
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false
+csharp_style_var_for_built_in_types = false
+csharp_style_var_when_type_is_apparent = false
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true
+csharp_style_expression_bodied_constructors = false
+csharp_style_expression_bodied_indexers = true
+csharp_style_expression_bodied_lambdas = true
+csharp_style_expression_bodied_local_functions = false
+csharp_style_expression_bodied_methods = false
+csharp_style_expression_bodied_operators = false
+csharp_style_expression_bodied_properties = true
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_extended_property_pattern = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true
+
+# Modifier preferences
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
+csharp_style_prefer_readonly_struct = true
+csharp_style_prefer_readonly_struct_member = true
+
+# Code-block preferences
+csharp_prefer_braces = true
+csharp_prefer_simple_using_statement = true
+csharp_style_namespace_declarations = block_scoped
+csharp_style_prefer_method_group_conversion = true
+csharp_style_prefer_primary_constructors = true
+csharp_style_prefer_top_level_statements = true
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true
+csharp_style_deconstructed_variable_declaration = true
+csharp_style_implicit_object_creation_when_type_is_apparent = true
+csharp_style_inlined_variable_declaration = true
+csharp_style_prefer_index_operator = true
+csharp_style_prefer_local_over_anonymous_function = true
+csharp_style_prefer_null_check_over_type_check = true
+csharp_style_prefer_range_operator = true
+csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_utf8_string_literals = true
+csharp_style_throw_expression = true
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace
+
+# New line preferences
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
+csharp_style_allow_embedded_statements_on_same_line_experimental = true
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers = 
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers = 
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers = 
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix = 
+dotnet_naming_style.pascal_case.required_suffix = 
+dotnet_naming_style.pascal_case.word_separator = 
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix = 
+dotnet_naming_style.begins_with_i.word_separator = 
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+
+dotnet_diagnostic.IDE0055.severity = warning

+ 2 - 2
LICENSE

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2024 Annulus Games
+Copyright (c) 2025 Yusuke Nakada
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+SOFTWARE.

+ 10 - 4
src/Lua/CodeAnalysis/Syntax/Lexer.cs

@@ -324,7 +324,13 @@ public ref struct Lexer
                 if (c is '\\')
                 {
                     Advance(1);
+
                     if (span.Length <= offset) break;
+                    if (span[offset] == '\r')
+                    {
+                        if (span.Length<=offset +1) continue;
+                        if (span[offset+1] == '\n')Advance(1);
+                    }
                 }
                 else if (c == quote)
                 {
@@ -530,8 +536,8 @@ public ref struct Lexer
     static bool IsIdentifier(char c)
     {
         return c == '_' ||
-            ('A' <= c && c <= 'Z') ||
-            ('a' <= c && c <= 'z') ||
-            StringHelper.IsNumber(c);
+               ('A' <= c && c <= 'Z') ||
+               ('a' <= c && c <= 'z') ||
+               StringHelper.IsNumber(c);
     }
-}
+}

+ 14 - 19
src/Lua/Internal/HexConverter.cs

@@ -86,25 +86,20 @@ public static class HexConverter
 
     static int ToInt(char c)
     {
-        return c switch
+        unchecked
         {
-            '0' => 0,
-            '1' => 1,
-            '2' => 2,
-            '3' => 3,
-            '4' => 4,
-            '5' => 5,
-            '6' => 6,
-            '7' => 7,
-            '8' => 8,
-            '9' => 9,
-            'A' or 'a' => 10,
-            'B' or 'b' => 11,
-            'C' or 'd' => 12,
-            'D' or 'e' => 13,
-            'E' or 'e' => 14,
-            'F' or 'f' => 15,
-            _ => 0
-        };
+            switch (c)
+            {
+                case < '0':
+                    return 0;
+                case <= '9':
+                    return (c - '0');
+                case >= 'A' and <= 'F':
+                    return (c - 'A' + 10);
+                case >= 'a' and <= 'f':
+                    return (c - 'a' + 10);
+            }
+        }
+        return 0;
     }
 }

+ 22 - 1
src/Lua/Internal/MathEx.cs

@@ -1,5 +1,7 @@
 using System.Runtime.CompilerServices;
-
+#if NET6_0_OR_GREATER
+using System.Numerics;
+#endif
 namespace Lua;
 
 internal static class MathEx
@@ -86,4 +88,23 @@ internal static class MathEx
     {
         return ((int)Math.Truncate(d), d % 1.0);
     }
+    
+    /// <summary>Returns the smallest power of two greater than or equal to the input.</summary>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static int NextPowerOfTwo(int x)
+    {
+#if NET6_0_OR_GREATER
+        if (x <= 0) return 0;
+        return (int)BitOperations.RoundUpToPowerOf2((uint)x);
+#else
+        if (x <= 0) return 0;
+        x -= 1;
+        x |= x >> 1;
+        x |= x >> 2;
+        x |= x >> 4;
+        x |= x >> 8;
+        x |= x >> 16;
+        return x + 1;
+#endif
+    }
 }

+ 4 - 3
src/Lua/Internal/StringHelper.cs

@@ -37,7 +37,7 @@ internal static class StringHelper
                         builder.Append('\n');
                         break;
                     case '\r':
-                        builder.Append('\r');
+                        builder.Append('\n');
                         // check CRLF
                         if (i + 1 < literal.Length && literal[i + 1] is '\n')
                         {
@@ -306,6 +306,7 @@ internal static class StringHelper
                             builder.Append(c);
                             break;
                     }
+
                     isEscapeSequence = false;
                 }
             }
@@ -358,7 +359,7 @@ internal static class StringHelper
     public static bool IsDigit(char c)
     {
         return IsNumber(c) ||
-            ('a' <= c && c <= 'f') ||
-            ('A' <= c && c <= 'F');
+               ('a' <= c && c <= 'f') ||
+               ('A' <= c && c <= 'F');
     }
 }

+ 19 - 6
src/Lua/LuaTable.cs

@@ -20,6 +20,9 @@ public sealed class LuaTable
     LuaTable? metatable;
 
     internal LuaValueDictionary Dictionary => dictionary;
+    private const int MaxArraySize = 1 << 24;
+    private const int MaxDistance = 1 << 12;
+
 
     public LuaValue this[LuaValue key]
     {
@@ -53,7 +56,15 @@ public sealed class LuaTable
                 if (MathEx.IsInteger(d))
                 {
                     var index = (int)d;
-                    if (0 < index && index <= Math.Max(array.Length * 2, 8))
+                    
+                    var distance = index - array.Length;
+                    if (distance > MaxDistance)
+                    {
+                        dictionary[key] = value;
+                        return;
+                    }
+                    
+                    if (0 < index && index < MaxArraySize && index <= Math.Max(array.Length * 2, 8))
                     {
                         if (array.Length < index)
                             EnsureArrayCapacity(index);
@@ -170,6 +181,12 @@ public sealed class LuaTable
         }
 
         var arrayIndex = index - 1;
+        var distance = index - array.Length;
+        if (distance > MaxDistance)
+        {
+            dictionary[index] = value;
+            return;
+        }
 
         if (index > array.Length || array[^1].Type != LuaValueType.Nil)
         {
@@ -251,11 +268,7 @@ public sealed class LuaTable
         var prevLength = array.Length;
         var newLength = array.Length;
         if (newLength == 0) newLength = 8;
-
-        while (newLength < newCapacity)
-        {
-            newLength *= 2;
-        }
+        newLength = newCapacity <= 8 ? 8 : MathEx.NextPowerOfTwo(newCapacity);
 
         Array.Resize(ref array, newLength);
 

+ 18 - 7
src/Lua/Standard/MathematicsLibrary.cs

@@ -224,23 +224,34 @@ public sealed class MathematicsLibrary
     public ValueTask<int> Random(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
         var rand = context.State.Environment[RandomInstanceKey].Read<RandomUserData>().Random;
-
+        // When we call it without arguments, it returns a pseudo-random real number with uniform distribution in the interval [0,1
         if (context.ArgumentCount == 0)
         {
             buffer.Span[0] = rand.NextDouble();
         }
+        // When we call it with only one argument, an integer n, it returns an integer pseudo-random number such that 1 <= x <= n.
+        // This is different from the C# random functions.
+        // See: https://www.lua.org/pil/18.html
         else if (context.ArgumentCount == 1)
         {
-            var arg0 = context.GetArgument<double>(0);
-            buffer.Span[0] = rand.NextDouble() * (arg0 - 1) + 1;
+            var arg0 = context.GetArgument<int>(0);
+            if (arg0 < 0)
+            {
+                LuaRuntimeException.BadArgument(context.State.GetTraceback(), 0, "random");
+            }
+            buffer.Span[0] = rand.Next(1, arg0 + 1);
         }
+        // Finally, we can call random with two integer arguments, l and u, to get a pseudo-random integer x such that l <= x <= u.
         else
         {
-            var arg0 = context.GetArgument<double>(0);
-            var arg1 = context.GetArgument<double>(1);
-            buffer.Span[0] = rand.NextDouble() * (arg1 - arg0) + arg0;
+            var arg0 = context.GetArgument<int>(0);
+            var arg1 = context.GetArgument<int>(1);
+            if (arg0 < 1 || arg1 <= arg0)
+            {
+                LuaRuntimeException.BadArgument(context.State.GetTraceback(), 1, "random");
+            }
+            buffer.Span[0] = rand.Next(arg0, arg1 + 1);
         }
-
         return new(1);
     }
 

+ 5 - 1
tests/Lua.Tests/HexConverterTests.cs

@@ -7,8 +7,12 @@ public class HexConverterTests
     [TestCase("0x10", 16)]
     [TestCase("0x0p12", 0)]
     [TestCase("-0x1.0p-1", -0.5)]
+    [TestCase("0x0.1e", 0.1171875)]
+    [TestCase("0xA23p-4", 162.1875)]
+    [TestCase("0X1.921FB54442D18P+1", 3.1415926535898)]
+    [TestCase("0X1.bcde19p+1", 3.475527882576)]
     public void Test_ToDouble(string text, double expected)
     {
-        Assert.That(HexConverter.ToDouble(text), Is.EqualTo(expected));
+        Assert.That(Math.Abs(HexConverter.ToDouble(text) - expected), Is.LessThanOrEqualTo(0.00001d));
     }
 }

+ 17 - 0
tests/Lua.Tests/StringTests.cs

@@ -0,0 +1,17 @@
+using Lua.CodeAnalysis.Syntax;
+using Lua.CodeAnalysis.Syntax.Nodes;
+
+namespace Lua.Tests;
+
+public class StringTests
+{
+    [TestCase("\r")]
+    [TestCase("\n")]
+    [TestCase("\r\n")]
+    public async Task Test_ShortString_RealNewLine(string newLine)
+    {
+        var result = await LuaState.Create().DoStringAsync($"return \"\\{newLine}\"");
+        Assert.That(result, Has.Length.EqualTo(1));
+        Assert.That(result[0], Is.EqualTo(new LuaValue("\n")));
+    }
+}

+ 21 - 0
tests/Lua.Tests/TableTests.cs

@@ -41,4 +41,25 @@ public class TableTests
         Assert.That(value, Is.EqualTo(new LuaValue(2)));
         Assert.That(table[2], Is.EqualTo(new LuaValue(3)));
     }
+
+    [Test]
+    public void Test_TableResize()
+    {
+        var table = new LuaTable();
+        int i = 1;
+        int count = 10000;
+        while (count > 0)
+        {
+            var key = i;
+            table[key] = key;
+            table[key * 2 - key / 2] = key;
+            i += key;
+            count--;
+        }
+
+        table[1] = 0;
+        table[int.MaxValue - 1] = 0;
+        Assert.That(table[1], Is.EqualTo(new LuaValue(0)));
+        Assert.That(table[int.MaxValue - 1], Is.EqualTo(new LuaValue(0)));
+    }
 }