@@ -2054,6 +2054,7 @@ def swap_symbolic_calcs(
20542054 functions_on_symbolic_expressions = [
20552055 insert_parentheses ,
20562056 swap_custom_symbols ,
2057+ swap_custom_brackets ,
20572058 swap_math_funcs ,
20582059 swap_superscripts ,
20592060 swap_chained_fracs ,
@@ -2164,15 +2165,117 @@ def swap_custom_symbols(d: deque, **config_options) -> deque:
21642165 custom_symbols = config_options .get ("custom_symbols" , {})
21652166 new_item = item
21662167 for symbol , latex_symbol in custom_symbols .items ():
2167- if symbol in item :
2168- new_item = item .replace (symbol , latex_symbol )
2169- break
2168+ if (
2169+ symbol in new_item
2170+ ): # Changed to new_item to allow changes to a string accumulate
2171+ new_item = new_item .replace (
2172+ symbol , latex_symbol
2173+ ) # Changed to new_item to allow changes to a string accumulate
2174+ # Removed break to permit multiple replacements
21702175 swapped_items .append (new_item )
21712176 else :
21722177 swapped_items .append (item )
21732178 return swapped_items
21742179
21752180
2181+ def swap_custom_brackets (d : deque , ** config_options ) -> deque :
2182+ """
2183+ Swaps custom bracket character or string with their corresponding LaTeX brackets.
2184+ Set with user defined dictionary 'custom_brackets' in config_options
2185+
2186+ Example usage:
2187+
2188+ handcalcs.set_option("custom_brackets", {
2189+ "parenthesis": 'ˉ', # Replace ˉ with consecutively alternating ( and ) characters
2190+ "square_brackets": 'ˍ' # Replaces ˍ with consecutively alternating [ and ] characters
2191+ })
2192+
2193+ Resulting behavior:
2194+ 'myvarˉ0ˉ' -> 'myvar(0)'
2195+ 'myvarˍ0ˍ' -> 'myvar[0]'
2196+
2197+ Supported bracket types:
2198+ - parenthesis
2199+ - square_brackets
2200+ - angle_brackets
2201+ - curly_brackets
2202+ - pipes
2203+ - double_pipes
2204+ """
2205+ swapped_items = deque ([])
2206+ for item in d :
2207+ if isinstance (item , deque ):
2208+ new_item = swap_custom_brackets (item , ** config_options )
2209+ swapped_items .append (new_item )
2210+ elif isinstance (item , str ):
2211+ custom_brackets = config_options .get ("custom_brackets" , {})
2212+ new_item = item
2213+
2214+ # Process each bracket type
2215+ for bracket_type , custom_str in custom_brackets .items ():
2216+ if custom_str and custom_str in new_item :
2217+ if bracket_type == "parenthesis" :
2218+ new_item = _replace_alternating_brackets (
2219+ new_item , custom_str , "(" , ")"
2220+ )
2221+ elif bracket_type == "square_brackets" :
2222+ new_item = _replace_alternating_brackets (
2223+ new_item , custom_str , "[" , "]"
2224+ )
2225+ elif bracket_type == "angle_brackets" :
2226+ new_item = _replace_alternating_brackets (
2227+ new_item , custom_str , r"\langle" , r"\rangle"
2228+ )
2229+ elif bracket_type == "curly_brackets" :
2230+ new_item = _replace_alternating_brackets (
2231+ new_item , custom_str , r"\lbrace" , r"\rbrace"
2232+ )
2233+ elif bracket_type == "pipes" :
2234+ new_item = _replace_alternating_brackets (
2235+ new_item , custom_str , "|" , "|"
2236+ )
2237+ elif bracket_type == "double_pipes" :
2238+ new_item = _replace_alternating_brackets (
2239+ new_item , custom_str , r"\|" , r"\|"
2240+ )
2241+
2242+ swapped_items .append (new_item )
2243+ else :
2244+ swapped_items .append (item )
2245+ return swapped_items
2246+
2247+
2248+ def _replace_alternating_brackets (
2249+ text : str , custom_str : str , left_bracket : str , right_bracket : str
2250+ ) -> str :
2251+ """
2252+ Helper function to replace alternating occurrences of custom_str with left and right brackets.
2253+ Supports both single and multi-character custom_str strings.
2254+ """
2255+ if custom_str not in text :
2256+ return text
2257+
2258+ result = ""
2259+ is_left = True # Start with left bracket
2260+ i = 0
2261+ str_len = len (custom_str )
2262+
2263+ while i < len (text ):
2264+ # Check if we're at the start of a custom_str occurrence
2265+ if text [i : i + str_len ] == custom_str :
2266+ if is_left :
2267+ result += left_bracket
2268+ else :
2269+ result += right_bracket
2270+ is_left = not is_left # Toggle for next occurrence
2271+ i += str_len # Skip past the entire custom_str
2272+ else :
2273+ result += text [i ]
2274+ i += 1
2275+
2276+ return result
2277+
2278+
21762279def swap_log_func (d : deque , calc_results : dict , ** config_options ) -> deque :
21772280 """
21782281 Returns a new deque representing 'd' but with any log functions swapped
@@ -2400,21 +2503,20 @@ def extend_subscripts(pycode_as_deque: deque, **config_options) -> deque:
24002503 """
24012504 swapped_deque = deque ([])
24022505 for item in pycode_as_deque :
2403- discount = 0 # hack to prevent excess braces from swap_long_var_str
24042506 if isinstance (item , deque ):
24052507 new_item = extend_subscripts (item ) # recursion!
24062508 swapped_deque .append (new_item )
24072509 elif isinstance (item , str ) and "_" in item and not "\\ int" in item :
2408- if "\\ mathrm{" in item or "\\ operatorname{" in item :
2409- discount = 1
24102510 new_item = ""
24112511 for char in item :
24122512 if char == "_" :
24132513 new_item += char
24142514 new_item += "{"
24152515 else :
24162516 new_item += char
2417- num_braces = new_item .count ("{" ) - discount
2517+ num_braces = new_item .count ("{" ) - new_item .count (
2518+ "}"
2519+ ) # count unclosed braces
24182520
24192521 new_item += "}" * num_braces
24202522 swapped_deque .append (new_item )
0 commit comments