who-are-you

Finished
2626
2727
2828def parse(input_string: str) -> SgfTree:
29 pass
29 """Parse an SGF string and return a tree structure of properties."""
30 # Edge Case: Empty input
31 if not input_string:
32 raise ValueError("tree missing")
33
34 # Edge Case: Input doesn't start with '('
35 if not input_string.startswith('('):
36 raise ValueError("tree missing")
37
38 # Edge Case: Input doesn't end with ')'
39 if not input_string.endswith(')'):
40 raise ValueError("tree missing")
41
42 # Parse the content inside the outermost parentheses
43 content = input_string[1:-1]
44
45 # Edge Case: Empty tree with no nodes
46 if not content:
47 raise ValueError("tree with no nodes")
48
49 # Edge Case: Content doesn't start with ';'
50 if not content.startswith(';'):
51 raise ValueError("tree with no nodes")
52
53 # Parse the tree
54 tree, _ = parse_tree(content)
55 return tree
56
57
58def parse_tree(content: str) -> tuple[SgfTree, int]:
59 """Parse a tree from the content string."""
60 # Edge Case: Content is empty
61 if not content:
62 raise ValueError("tree with no nodes")
63
64 # Edge Case: Content doesn't start with ';'
65 if not content.startswith(';'):
66 raise ValueError("tree with no nodes")
67
68 # Parse the node
69 node, pos = parse_node(content)
70
71 children = []
72
73 # Parse any children
74 while pos < len(content):
75 if content[pos] == '(': # Start of a child tree
76 # Find the matching closing parenthesis for this child
77 paren_count = 1
78 child_start = pos + 1
79 child_end = child_start
80
81 while child_end < len(content) and paren_count > 0:
82 if content[child_end] == '(':
83 paren_count += 1
84 elif content[child_end] == ')':
85 paren_count -= 1
86 child_end += 1
87
88 # Edge Case: Unmatched parenthesis
89 if paren_count != 0:
90 raise ValueError("tree missing")
91
92 # Parse the child tree content (without the outer parentheses)
93 child_content = content[child_start:child_end-1]
94 child_tree, _ = parse_tree(child_content)
95 children.append(child_tree)
96 pos = child_end
97 elif content[pos] == ')': # End of current tree
98 break
99 elif content[pos] == ';': # Another node in the same tree (shorthand)
100 child_tree, new_pos = parse_node(content[pos:])
101 children.append(child_tree)
102 pos += new_pos
103 else:
104 # Edge Case: Unexpected character
105 raise ValueError("tree missing")
106
107 return SgfTree(properties=node.properties, children=children), pos
108
109
110def parse_node(content: str) -> tuple[SgfTree, int]:
111 """Parse a node from the content string."""
112 # Edge Case: Content is empty
113 if not content:
114 raise ValueError("tree with no nodes")
115
116 # Edge Case: Node doesn't start with ';'
117 if not content.startswith(';'):
118 raise ValueError("tree with no nodes")
119
120 properties = {}
121 pos = 1 # Skip the ';'
122
123 # Parse properties
124 while pos < len(content) and content[pos].isalpha():
125 # Parse property key
126 key_start = pos
127 while pos < len(content) and content[pos].isalpha():
128 pos += 1
129 key = content[key_start:pos]
130
131 # Edge Case: Property key is not uppercase
132 if not key.isupper():
133 raise ValueError("property must be in uppercase")
134
135 # Parse property values
136 values = []
137 while pos < len(content) and content[pos] == '[':
138 value, value_end_pos = parse_value(content[pos+1:])
139 values.append(value)
140 pos += value_end_pos + 2 # Skip '[' and ']'
141
142 # Edge Case: Property has no values
143 if not values:
144 raise ValueError("properties without delimiter")
145
146 properties[key] = values
147
148 # Skip any whitespace after property values
149 while pos < len(content) and content[pos].isspace():
150 pos += 1
151
152 return SgfTree(properties=properties), pos
153
154
155def parse_value(content: str) -> tuple[str, int]:
156 """Parse a value from the content string according to SGF Text type rules."""
157 value = ""
158 pos = 0
159
160 while pos < len(content):
161 if content[pos] == ']': # End of value
162 break
163 elif content[pos] == '\\': # Escape character
164 pos += 1
165 # Edge Case: Escape at end of content
166 if pos >= len(content):
167 value += '\\'
168 break
169
170 char = content[pos]
171 # According to SGF spec: Any non-whitespace character after \ is inserted as-is
172 # Any whitespace character after \ follows the normal rules
173 if char.isspace():
174 # Whitespace after escape follows normal rules
175 if char == '\n':
176 # Newline immediately after \ is removed (both \ and \n)
177 pos += 1
178 continue
179 else:
180 # Other whitespace becomes space
181 value += ' '
182 else:
183 # Non-whitespace character is inserted as-is
184 value += char
185 pos += 1
186 elif content[pos] == '\n':
187 # Newlines are preserved unless immediately after \
188 value += content[pos]
189 elif content[pos].isspace():
190 # All whitespace other than newline becomes space
191 value += ' '
192 else:
193 value += content[pos]
194 pos += 1
195
196 # Edge Case: Unclosed value
197 if pos >= len(content) or content[pos] != ']':
198 raise ValueError("properties without delimiter")
199
200 return value, pos
201
202# Handled Edge Cases: Empty input, Input doesn't start with '(', Input doesn't end with ')', Empty tree with no nodes, Content doesn't start with ';', Unmatched parenthesis, Unexpected character, Property key is not uppercase, Property has no values, Escape at end of content, Unclosed value
Test NameStatus
test_escaped_backslash_in_property_value_becomes_just_a_backslash
Fail
test_escaped_closing_bracket_within_property_value_becomes_just_a_closing_bracket
Fail
test_escaped_t_and_n_in_property_value_are_just_letters_not_whitespace
Fail
test_mixing_various_kinds_of_whitespace_and_escaped_characters_in_property_value
Fail
test_opening_bracket_within_property_value_doesn_t_need_to_be_escaped
Fail
test_all_lowercase_property
Pass
test_empty_input
Pass
test_escaped_newline_in_property_value_is_converted_to_nothing_at_all
Pass
test_escaped_tab_in_property_value_is_converted_to_space
Pass
test_multiple_properties
Pass
test_multiple_property_values
Pass
test_node_without_properties
Pass
test_node_without_tree
Pass
test_parentheses_in_property_value_don_t_need_to_be_escaped
Pass
test_properties_without_delimiter
Pass
test_semicolon_in_property_value_doesn_t_need_to_be_escaped
Pass
test_single_node_tree
Pass
test_tree_with_no_nodes
Pass
test_two_child_trees
Pass
test_two_nodes
Pass
test_upper_and_lowercase_property
Pass
test_within_property_values_newlines_remain_as_newlines
Pass
test_within_property_values_whitespace_characters_such_as_tab_are_converted_to_spaces
Pass

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