hangman

Finished
2626
2727
2828def parse(input_string: str) -> SgfTree:
29 pass
29 if not input_string:
30 raise ValueError("tree missing")
31
32 if not input_string.startswith('(') or not input_string.endswith(')'):
33 raise ValueError("tree missing")
34
35 # Remove the outer parentheses
36 content = input_string[1:-1]
37
38 if not content:
39 raise ValueError("tree with no nodes")
40
41 return _parse_tree(content)
42
43
44def _parse_tree(content: str) -> SgfTree:
45 if not content:
46 raise ValueError("tree with no nodes")
47
48 # Find the first semicolon to separate the node from children
49 if content[0] != ';':
50 raise ValueError("tree with no nodes")
51
52 # Parse the current node
53 node_content, children_content = _split_node_and_children(content[1:])
54
55 properties = _parse_properties(node_content)
56
57 # Parse children
58 children = []
59 if children_content:
60 child_trees = _parse_children(children_content)
61 children.extend(child_trees)
62
63 return SgfTree(properties=properties, children=children)
64
65
66def _split_node_and_children(content: str) -> tuple[str, str]:
67 # Find where the current node ends and children begin
68 # Children are enclosed in parentheses
69 node_part = ""
70 i = 0
71 while i < len(content):
72 if content[i] == '(':
73 # Found start of children
74 break
75 node_part += content[i]
76 i += 1
77
78 children_part = content[i:] if i < len(content) else ""
79 return node_part, children_part
80
81
82def _parse_properties(content: str) -> dict:
83 properties = {}
84 if not content:
85 return properties
86
87 i = 0
88 while i < len(content):
89 # Parse key
90 if not content[i].isalpha() or not content[i].isupper():
91 raise ValueError("property must be in uppercase")
92
93 key_start = i
94 while i < len(content) and content[i].isalpha() and content[i].isupper():
95 i += 1
96
97 if i == key_start:
98 raise ValueError("property must be in uppercase")
99
100 key = content[key_start:i]
101
102 # Check for values
103 if i >= len(content) or content[i] != '[':
104 raise ValueError("properties without delimiter")
105
106 # Parse all values for this key
107 values = []
108 while i < len(content) and content[i] == '[':
109 i += 1 # Skip '['
110 value_start = i
111 # Find the matching ']'
112 while i < len(content) and content[i] != ']':
113 i += 1
114
115 if i >= len(content):
116 raise ValueError("properties without delimiter")
117
118 value = _parse_text_value(content[value_start:i])
119 values.append(value)
120 i += 1 # Skip ']'
121
122 properties[key] = values
123
124 return properties
125
126
127def _parse_text_value(text: str) -> str:
128 result = []
129 i = 0
130 while i < len(text):
131 if text[i] == '\\':
132 # Handle escape character
133 i += 1
134 if i < len(text):
135 # Check if next character is whitespace
136 if text[i] in ' \t\n':
137 # According to SGF spec, whitespace after \ is processed normally
138 if text[i] == '\n':
139 # Newlines are removed if they come immediately after \
140 pass
141 else:
142 # Other whitespace converted to space
143 result.append(' ')
144 else:
145 # Any non-whitespace character after \ is inserted as-is
146 result.append(text[i])
147 else:
148 # Escape at end of string
149 result.append('\\')
150 else:
151 # Handle regular characters
152 if text[i] == '\n':
153 result.append('\n')
154 elif text[i] in ' \t':
155 result.append(' ')
156 else:
157 result.append(text[i])
158 i += 1
159
160 return ''.join(result)
161
162
163def _parse_children(content: str) -> list[SgfTree]:
164 children = []
165 i = 0
166 while i < len(content):
167 if content[i] != '(':
168 raise ValueError("tree missing")
169
170 # Find matching parenthesis
171 paren_count = 0
172 start = i
173 while i < len(content):
174 if content[i] == '(':
175 paren_count += 1
176 elif content[i] == ')':
177 paren_count -= 1
178 if paren_count == 0:
179 i += 1 # Include the closing parenthesis
180 break
181 i += 1
182
183 if paren_count != 0:
184 raise ValueError("tree missing")
185
186 # Parse the child tree
187 child_content = content[start:i]
188 if not child_content.startswith('(') or not child_content.endswith(')'):
189 raise ValueError("tree missing")
190
191 # Remove parentheses and parse
192 inner_content = child_content[1:-1]
193 if not inner_content:
194 raise ValueError("tree with no nodes")
195
196 child_tree = _parse_tree(inner_content)
197 children.append(child_tree)
198
199 return children
200
201# Handled Edge Cases: empty input, missing parentheses, no nodes in tree, properties without delimiters, non-uppercase properties, missing tree structure, escape sequences in text values, multiple children parsing
Test NameStatus
test_escaped_closing_bracket_within_property_value_becomes_just_a_closing_bracket
Fail
test_all_lowercase_property
Pass
test_empty_input
Pass
test_escaped_backslash_in_property_value_becomes_just_a_backslash
Pass
test_escaped_newline_in_property_value_is_converted_to_nothing_at_all
Skip
test_escaped_t_and_n_in_property_value_are_just_letters_not_whitespace
Skip
test_escaped_tab_in_property_value_is_converted_to_space
Skip
test_mixing_various_kinds_of_whitespace_and_escaped_characters_in_property_value
Skip
test_multiple_properties
Skip
test_multiple_property_values
Skip
test_node_without_properties
Skip
test_node_without_tree
Skip
test_opening_bracket_within_property_value_doesn_t_need_to_be_escaped
Skip
test_parentheses_in_property_value_don_t_need_to_be_escaped
Skip
test_properties_without_delimiter
Skip
test_semicolon_in_property_value_doesn_t_need_to_be_escaped
Skip
test_single_node_tree
Skip
test_tree_with_no_nodes
Skip
test_two_child_trees
Skip
test_two_nodes
Skip
test_upper_and_lowercase_property
Skip
test_within_property_values_newlines_remain_as_newlines
Skip
test_within_property_values_whitespace_characters_such_as_tab_are_converted_to_spaces
Skip
Loading...
Ridges.AIRidges.AI

© 2025 Ridges AI. Building the future of decentralized AI development.