Skip to content

Commit 985d9e7

Browse files
NSHkrNSHkr
authored andcommitted
prog refactor l3
1 parent 9643625 commit 985d9e7

File tree

4 files changed

+601
-439
lines changed

4 files changed

+601
-439
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
defmodule JsonRemedy.Layer3.BinaryProcessors do
2+
@moduledoc """
3+
Binary pattern matching optimization functions for Layer 3 syntax normalization.
4+
5+
Contains optimized binary processing functions that eliminate String.at/2 calls
6+
for maximum performance.
7+
"""
8+
9+
alias JsonRemedy.Layer3.SyntaxHelpers
10+
11+
@doc """
12+
Determine next expecting state after simple transitions.
13+
"""
14+
@spec determine_next_expecting_simple(atom(), list()) :: atom()
15+
def determine_next_expecting_simple(_expecting, _stack), do: :value
16+
17+
@doc """
18+
Determine expecting state after closing delimiters.
19+
"""
20+
@spec determine_expecting_after_close_simple(list()) :: atom()
21+
def determine_expecting_after_close_simple([]), do: :value
22+
def determine_expecting_after_close_simple([:object | _]), do: :value
23+
def determine_expecting_after_close_simple([:array | _]), do: :value
24+
def determine_expecting_after_close_simple(_), do: :value
25+
26+
@doc """
27+
Process identifiers with binary pattern matching.
28+
"""
29+
@spec process_identifier_binary_simple(
30+
binary(),
31+
iolist(),
32+
list(),
33+
boolean(),
34+
boolean(),
35+
String.t() | nil,
36+
list(),
37+
atom(),
38+
non_neg_integer()
39+
) :: {binary(), iolist(), list(), boolean(), boolean(), String.t() | nil, list(), atom(), non_neg_integer()}
40+
def process_identifier_binary_simple(
41+
binary,
42+
result_iolist,
43+
repairs,
44+
in_string,
45+
escape_next,
46+
quote,
47+
stack,
48+
expecting,
49+
pos
50+
) do
51+
{identifier, remaining, chars_consumed} = consume_identifier_binary_simple(binary)
52+
53+
{result_addition, new_repairs} =
54+
case identifier do
55+
"True" ->
56+
repair = SyntaxHelpers.create_repair("normalized boolean", "Normalized boolean True -> true", pos)
57+
{"true", [repair]}
58+
59+
"TRUE" ->
60+
repair = SyntaxHelpers.create_repair("normalized boolean", "Normalized boolean TRUE -> true", pos)
61+
{"true", [repair]}
62+
63+
"False" ->
64+
repair = SyntaxHelpers.create_repair("normalized boolean", "Normalized boolean False -> false", pos)
65+
{"false", [repair]}
66+
67+
"FALSE" ->
68+
repair = SyntaxHelpers.create_repair("normalized boolean", "Normalized boolean FALSE -> false", pos)
69+
{"false", [repair]}
70+
71+
"None" ->
72+
repair = SyntaxHelpers.create_repair("normalized null", "Normalized None -> null", pos)
73+
{"null", [repair]}
74+
75+
"NULL" ->
76+
repair = SyntaxHelpers.create_repair("normalized null", "Normalized NULL -> null", pos)
77+
{"null", [repair]}
78+
79+
"Null" ->
80+
repair = SyntaxHelpers.create_repair("normalized null", "Normalized Null -> null", pos)
81+
{"null", [repair]}
82+
83+
_ when expecting == :key ->
84+
repair =
85+
SyntaxHelpers.create_repair(
86+
"quoted unquoted key",
87+
"Added quotes around unquoted key '#{identifier}'",
88+
pos
89+
)
90+
91+
{"\"" <> identifier <> "\"", [repair]}
92+
93+
_ ->
94+
{identifier, []}
95+
end
96+
97+
new_expecting = determine_next_expecting_simple(expecting, stack)
98+
99+
{remaining, [result_iolist, result_addition], new_repairs ++ repairs, in_string, escape_next, quote, stack, new_expecting, pos + chars_consumed}
100+
end
101+
102+
@doc """
103+
Process numbers with binary pattern matching.
104+
"""
105+
@spec process_number_binary_simple(
106+
binary(),
107+
iolist(),
108+
list(),
109+
boolean(),
110+
boolean(),
111+
String.t() | nil,
112+
list(),
113+
atom(),
114+
non_neg_integer()
115+
) :: {binary(), iolist(), list(), boolean(), boolean(), String.t() | nil, list(), atom(), non_neg_integer()}
116+
def process_number_binary_simple(
117+
binary,
118+
result_iolist,
119+
repairs,
120+
in_string,
121+
escape_next,
122+
quote,
123+
stack,
124+
expecting,
125+
pos
126+
) do
127+
{number, remaining, chars_consumed} = consume_number_binary_simple(binary)
128+
129+
new_expecting = determine_next_expecting_simple(expecting, stack)
130+
131+
{remaining, [result_iolist, number], repairs, in_string, escape_next, quote, stack, new_expecting, pos + chars_consumed}
132+
end
133+
134+
@doc """
135+
Binary pattern matching for identifier consumption - UTF-8 safe.
136+
"""
137+
@spec consume_identifier_binary_simple(binary()) :: {String.t(), binary(), non_neg_integer()}
138+
def consume_identifier_binary_simple(binary),
139+
do: consume_identifier_binary_simple(binary, <<>>, 0)
140+
141+
defp consume_identifier_binary_simple(<<char::utf8, rest::binary>>, acc, count)
142+
when (char >= ?a and char <= ?z) or (char >= ?A and char <= ?Z) or
143+
(char >= ?0 and char <= ?9) or char == ?_ or char > 127 do
144+
# UTF-8 safe: char > 127 allows all UTF-8 multi-byte characters
145+
consume_identifier_binary_simple(rest, <<acc::binary, char::utf8>>, count + 1)
146+
end
147+
148+
defp consume_identifier_binary_simple(remaining, acc, count) do
149+
{acc, remaining, count}
150+
end
151+
152+
@doc """
153+
Binary pattern matching for number consumption.
154+
"""
155+
@spec consume_number_binary_simple(binary()) :: {String.t(), binary(), non_neg_integer()}
156+
def consume_number_binary_simple(binary), do: consume_number_binary_simple(binary, <<>>, 0)
157+
158+
defp consume_number_binary_simple(<<char::utf8, rest::binary>>, acc, count)
159+
when (char >= ?0 and char <= ?9) or char == ?. or char == ?- or char == ?+ or
160+
char == ?e or char == ?E do
161+
consume_number_binary_simple(rest, <<acc::binary, char::utf8>>, count + 1)
162+
end
163+
164+
defp consume_number_binary_simple(remaining, acc, count) do
165+
{acc, remaining, count}
166+
end
167+
end
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
defmodule JsonRemedy.Layer3.ContextManager do
2+
@moduledoc """
3+
Context management functions for Layer 3 syntax normalization.
4+
5+
Handles parsing state, expectation tracking, and context transitions
6+
during character-by-character processing.
7+
"""
8+
9+
@doc """
10+
Determine what to expect next based on current state.
11+
"""
12+
@spec determine_next_expecting(map()) :: atom()
13+
def determine_next_expecting(state) do
14+
case List.first(state.context_stack) do
15+
:object -> :comma_or_end
16+
:array -> :comma_or_end
17+
_ -> :value
18+
end
19+
end
20+
21+
@doc """
22+
Determine what to expect after closing a delimiter.
23+
"""
24+
@spec determine_expecting_after_close(list()) :: atom()
25+
def determine_expecting_after_close(stack) do
26+
case List.first(stack) do
27+
:object -> :comma_or_end
28+
:array -> :comma_or_end
29+
_ -> :value
30+
end
31+
end
32+
33+
@doc """
34+
Check if a position in the input is inside a string literal.
35+
Used to avoid applying repairs to string content.
36+
"""
37+
@spec inside_string?(String.t(), non_neg_integer()) :: boolean()
38+
def inside_string?(input, position)
39+
when is_binary(input) and is_integer(position) and position >= 0 do
40+
check_string_context(input, position, 0, false, false, nil)
41+
end
42+
43+
# Handle invalid inputs gracefully
44+
def inside_string?(nil, _position), do: false
45+
def inside_string?(_input, position) when not is_integer(position), do: false
46+
def inside_string?(_input, position) when position < 0, do: false
47+
def inside_string?(input, _position) when not is_binary(input), do: false
48+
49+
# Helper function to check if position is inside a string
50+
defp check_string_context(_input, position, current_pos, in_string, _escape_next, _quote)
51+
when current_pos >= position do
52+
in_string
53+
end
54+
55+
defp check_string_context(input, position, current_pos, in_string, escape_next, quote) do
56+
if current_pos >= String.length(input) do
57+
in_string
58+
else
59+
char = String.at(input, current_pos)
60+
61+
cond do
62+
escape_next ->
63+
check_string_context(input, position, current_pos + 1, in_string, false, quote)
64+
65+
in_string && char == "\\" ->
66+
check_string_context(input, position, current_pos + 1, in_string, true, quote)
67+
68+
in_string && char == quote ->
69+
check_string_context(input, position, current_pos + 1, false, false, nil)
70+
71+
in_string ->
72+
check_string_context(input, position, current_pos + 1, in_string, false, quote)
73+
74+
char == "\"" ->
75+
check_string_context(input, position, current_pos + 1, true, false, "\"")
76+
77+
char == "'" ->
78+
check_string_context(input, position, current_pos + 1, true, false, "'")
79+
80+
true ->
81+
check_string_context(input, position, current_pos + 1, false, false, nil)
82+
end
83+
end
84+
end
85+
86+
@doc """
87+
Get position information for error reporting.
88+
"""
89+
@spec get_position_info(String.t(), non_neg_integer()) ::
90+
%{line: pos_integer(), column: pos_integer(), context: String.t()}
91+
def get_position_info(input, position)
92+
when is_binary(input) and is_integer(position) and position >= 0 do
93+
lines = String.split(input, "\n")
94+
95+
{line_num, column, _} =
96+
Enum.reduce_while(lines, {1, 1, 0}, fn line, {current_line, _col, char_count} ->
97+
# +1 for newline
98+
line_length = String.length(line) + 1
99+
100+
if char_count + line_length > position do
101+
column = position - char_count + 1
102+
{:halt, {current_line, column, char_count}}
103+
else
104+
{:cont, {current_line + 1, 1, char_count + line_length}}
105+
end
106+
end)
107+
108+
context_start = max(0, position - 20)
109+
context_end = min(String.length(input), position + 20)
110+
context = String.slice(input, context_start, context_end - context_start)
111+
112+
%{
113+
line: line_num,
114+
column: column,
115+
context: context
116+
}
117+
end
118+
119+
# Handle invalid inputs gracefully
120+
def get_position_info(nil, _position) do
121+
%{line: 1, column: 1, context: ""}
122+
end
123+
124+
def get_position_info(_input, position) when not is_integer(position) or position < 0 do
125+
%{line: 1, column: 1, context: ""}
126+
end
127+
128+
def get_position_info(input, _position) when not is_binary(input) do
129+
%{line: 1, column: 1, context: inspect(input)}
130+
end
131+
end

0 commit comments

Comments
 (0)