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 # Remove outer 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 # Parse the tree
45 tree, pos = _parse_node(content, 0)
46
47 # Edge Case: Extra characters after parsing the main tree
48 if pos < len(content):
49 raise ValueError("tree missing")
50
51 return tree
52
53
54def _parse_node(content: str, pos: int) -> tuple[SgfTree, int]:
55 # Edge Case: Node doesn't start with ';'
56 if pos >= len(content) or content[pos] != ';':
57 raise ValueError("tree with no nodes")
58
59 pos += 1 # Skip ';'
60 properties = {}
61
62 # Parse properties
63 while pos < len(content) and content[pos].isalpha():
64 # Parse key
65 key_start = pos
66 while pos < len(content) and content[pos].isalpha():
67 pos += 1
68
69 key = content[key_start:pos]
70
71 # Edge Case: Key is not uppercase
72 if key != key.upper():
73 raise ValueError("property must be in uppercase")
74
75 # Edge Case: No values for the property
76 if pos >= len(content) or content[pos] != '[':
77 raise ValueError("properties without delimiter")
78
79 # Parse values
80 values = []
81 while pos < len(content) and content[pos] == '[':
82 pos += 1 # Skip '['
83 value_start = pos
84
85 # Parse value with escape handling
86 value_chars = []
87 while pos < len(content) and content[pos] != ']':
88 if content[pos] == '\\':
89 pos += 1
90 if pos >= len(content):
91 raise ValueError("properties without delimiter")
92
93 # Handle escaped characters according to SGF text rules
94 char = content[pos]
95 if char == 'n' and (pos+1 >= len(content) or content[pos+1] == ']'):
96 # Special case: \n at end is just n
97 value_chars.append('n')
98 elif char == 'n' and pos+1 < len(content) and content[pos+1] != ']':
99 # \n followed by something is newline + that something
100 value_chars.append('\n')
101 elif char == 't':
102 value_chars.append('t')
103 elif char in ' \t\r\n':
104 # Whitespace after \ follows normal rules
105 if char == '\n':
106 # Newline after \ is removed if immediately before ]
107 if pos+1 < len(content) and content[pos+1] == ']':
108 pass # Remove the newline
109 else:
110 value_chars.append(char)
111 else:
112 # Other whitespace becomes space
113 value_chars.append(' ')
114 else:
115 # Any other character after \ is inserted as-is
116 value_chars.append(char)
117 else:
118 char = content[pos]
119 if char in ' \t\r':
120 # All whitespace other than newline becomes space
121 value_chars.append(' ')
122 else:
123 value_chars.append(char)
124 pos += 1
125
126 # Edge Case: Unclosed value bracket
127 if pos >= len(content) or content[pos] != ']':
128 raise ValueError("properties without delimiter")
129
130 value = ''.join(value_chars)
131 values.append(value)
132 pos += 1 # Skip ']'
133
134 properties[key] = values
135
136 children = []
137
138 # Parse children
139 while pos < len(content) and content[pos] == '(':
140 child, pos = _parse_tree(content, pos)
141 children.append(child)
142
143 return SgfTree(properties, children), pos
144
145
146def _parse_tree(content: str, pos: int) -> tuple[SgfTree, int]:
147 # Edge Case: Tree doesn't start with '('
148 if pos >= len(content) or content[pos] != '(':
149 raise ValueError("tree missing")
150
151 pos += 1 # Skip '('
152
153 # Parse the main node
154 node, pos = _parse_node(content, pos)
155
156 # Edge Case: Tree doesn't end with ')'
157 if pos >= len(content) or content[pos] != ')':
158 raise ValueError("tree missing")
159
160 pos += 1 # Skip ')'
161
162 return node, pos
163# Handled Edge Cases: Empty input or doesn't start with '(', Doesn't end with ')', Empty content after removing parentheses, Extra characters after parsing the main tree, Node doesn't start with ';', Key is not uppercase, No values for the property, Unclosed value bracket, Tree doesn't start with '(', Tree doesn't end with ')'
Test NameStatus
test_escaped_newline_in_property_value_is_converted_to_nothing_at_all
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_parentheses_in_property_value_don_t_need_to_be_escaped
Fail
test_semicolon_in_property_value_doesn_t_need_to_be_escaped
Fail
test_two_nodes
Fail
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_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_properties_without_delimiter
Pass
test_single_node_tree
Pass
test_tree_with_no_nodes
Pass
test_two_child_trees
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.