tobe

Finished
2626
2727
2828def parse(input_string: str) -> SgfTree:
29 pass
29 # Edge Case: Empty input or doesn't start with '('
30 if not input_string or input_string[0] != '(':
31 raise ValueError("tree missing")
32
33 # Edge Case: Doesn't end with ')'
34 if input_string[-1] != ')':
35 raise ValueError("tree missing")
36
37 # Parse the content inside the outermost parentheses
38 content = input_string[1:-1]
39
40 # Edge Case: Empty content after removing parentheses
41 if not content:
42 raise ValueError("tree with no nodes")
43
44 # Check if it starts with a semicolon (node)
45 if content[0] != ';':
46 raise ValueError("tree with no nodes")
47
48 # Parse the first node and any subsequent variations
49 i = 1 # Start after the initial semicolon
50 properties = {}
51
52 # Parse properties of the current node
53 while i < len(content) and content[i] != '(' and content[i] != ';':
54 # Parse property key
55 key_start = i
56 while i < len(content) and content[i].isalpha():
57 i += 1
58
59 # Edge Case: Key is not all uppercase
60 key = content[key_start:i]
61 if not key.isupper():
62 raise ValueError("property must be in uppercase")
63
64 # Edge Case: No property values
65 if i >= len(content) or content[i] != '[':
66 raise ValueError("properties without delimiter")
67
68 # Parse property values
69 values = []
70 while i < len(content) and content[i] == '[':
71 i += 1 # Skip '['
72 value_start = i
73
74 # Parse value, handling escapes
75 value_chars = []
76 while i < len(content) and content[i] != ']':
77 if content[i] == '\\':
78 i += 1
79 if i >= len(content):
80 raise ValueError("properties without delimiter")
81
82 # Handle escaped characters according to SGF Text type rules
83 if content[i] == '\n':
84 # If newline comes immediately after backslash, remove it
85 pass
86 elif content[i].isspace():
87 # Any whitespace after \ is converted to space
88 value_chars.append(' ')
89 else:
90 # Any non-whitespace character after \ is inserted as-is
91 value_chars.append(content[i])
92 elif content[i] == '\n':
93 # Newlines remain as newlines unless escaped
94 value_chars.append(content[i])
95 elif content[i].isspace() and content[i] != '\n':
96 # All whitespace other than newline converted to space
97 value_chars.append(' ')
98 else:
99 value_chars.append(content[i])
100 i += 1
101
102 # Edge Case: Unclosed property value
103 if i >= len(content) or content[i] != ']':
104 raise ValueError("properties without delimiter")
105
106 value = ''.join(value_chars)
107 values.append(value)
108 i += 1 # Skip ']'
109
110 properties[key] = values
111
112 # Parse children
113 children = []
114
115 # Handle variations
116 while i < len(content) and content[i] == '(':
117 # Find matching closing parenthesis
118 paren_count = 1
119 start = i + 1
120 i += 1
121
122 while i < len(content) and paren_count > 0:
123 if content[i] == '(':
124 paren_count += 1
125 elif content[i] == ')':
126 paren_count -= 1
127 i += 1
128
129 # Edge Case: Unmatched parenthesis
130 if paren_count != 0:
131 raise ValueError("tree missing")
132
133 # Parse the subtree
134 subtree_content = content[start:i-1]
135 # Prepend ';' to make it a valid node
136 subtree_string = '(' + subtree_content + ')'
137 child_tree = parse(subtree_string)
138 children.append(child_tree)
139
140 # Handle linear children (after variations)
141 if i < len(content) and content[i] == ';':
142 # Parse the rest as a single child
143 rest_content = content[i:]
144 rest_string = '(' + rest_content + ')'
145 child_tree = parse(rest_string)
146 children.append(child_tree)
147
148 return SgfTree(properties, children)
149 # Handled Edge Cases: Empty input, missing parentheses, empty content, missing semicolon,
150 # lowercase property keys, missing delimiters, unclosed property values,
151 # unmatched parentheses
Test NameStatus
test_all_lowercase_property
Pass
test_empty_input
Pass
test_escaped_backslash_in_property_value_becomes_just_a_backslash
Pass
test_escaped_closing_bracket_within_property_value_becomes_just_a_closing_bracket
Pass
test_escaped_newline_in_property_value_is_converted_to_nothing_at_all
Pass
test_escaped_t_and_n_in_property_value_are_just_letters_not_whitespace
Pass
test_escaped_tab_in_property_value_is_converted_to_space
Pass
test_mixing_various_kinds_of_whitespace_and_escaped_characters_in_property_value
Pass
test_multiple_properties
Pass
test_multiple_property_values
Pass
test_node_without_properties
Pass
test_node_without_tree
Pass
test_opening_bracket_within_property_value_doesn_t_need_to_be_escaped
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.