prthm11 commited on
Commit
def05c9
·
verified ·
1 Parent(s): d101e85

Update utils/block_relation_builder.py

Browse files
Files changed (1) hide show
  1. utils/block_relation_builder.py +185 -225
utils/block_relation_builder.py CHANGED
@@ -260,7 +260,7 @@ all_block_definitions = {
260
  "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
261
  },
262
  "control_if_else": {
263
- "block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else",
264
  "functionality": "Executes one set of blocks if the specified boolean condition is true, and a different set of blocks if the condition is false. [NOTE: it takes boolean blocks as input]",
265
  "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
266
  },
@@ -1863,11 +1863,13 @@ def classify(line):
1863
 
1864
  # Control Blocks
1865
  # if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack"
1866
- if re.match(r"wait\s+(\(?\[?.+?\]?\)?)\s*(sec(?:ond)?s?)?$", l): return "control_wait", "stack"
1867
- if l.startswith("wait until <"): return "control_wait_until", "stack"
 
 
1868
  if l.startswith("repeat ("): return "control_repeat", "c_block"
1869
  if l == "forever": return "control_forever", "c_block"
1870
- if l.startswith("if <") and " then else" in l: return "control_if_else", "c_block"
1871
  if l.startswith("if <"): return "control_if", "c_block"
1872
  if l.startswith("repeat until <"): return "control_repeat_until", "c_block"
1873
  # Updated regex for stop block to handle different options
@@ -1924,38 +1926,26 @@ def generate_plan(generated_input, opcode_keys, pseudo_code):
1924
  """
1925
  Build a nested “plan” tree from:
1926
  • generated_input: dict of block_key -> block_data (pre-generated block definitions)
1927
- • opcode_keys: dict of opcode -> list of block_keys (in order)
1928
- • pseudo_code: a multiline string, indented with twospace levels
1929
 
1930
  Returns:
1931
  { "flow": [ ... list of block dictionaries ... ] }
1932
  """
1933
- # helper: pick next unused block_key for an opcode
1934
  ptrs = defaultdict(int)
1935
  def pick_key(opcode):
1936
  lst = opcode_keys.get(opcode, [])
1937
  idx = ptrs[opcode]
1938
  if idx >= len(lst):
1939
- # Fallback: if no more pre-generated keys, create a new one.
1940
- # This should ideally not happen if initial_opcode_counts is comprehensive
1941
  ptrs[opcode] += 1
1942
  return f"{opcode}_{idx + 1}"
1943
  ptrs[opcode] += 1
1944
  return lst[idx]
1945
 
1946
- all_generated_blocks = {} # This will store the final, structured blocks
1947
-
1948
- # Populate all_generated_blocks with initial blocks, ensuring they have IDs
1949
- for key, block_data in generated_input.items():
1950
- all_generated_blocks[key] = copy.deepcopy(block_data)
1951
- all_generated_blocks[key]["id"] = key # Ensure ID is set
1952
-
1953
- # Stack stores (indent, owner_block_id, last_block_in_current_linear_chain_id)
1954
- # owner_block_id: The ID of the C-block or Hat block that owns the current substack.
1955
- # last_block_in_current_linear_chain_id: The ID of the last block added to the *current linear sequence* within this substack.
1956
- stack = [(-2, None, None)] # Sentinel: (indent, owner_block_id, last_block_in_current_linear_chain_id)
1957
- # Using -2 for initial indent to be less than any valid indent (0 or more)
1958
 
 
1959
  top_level_script_keys = []
1960
 
1961
  lines = pseudo_code.splitlines()
@@ -1963,150 +1953,114 @@ def generate_plan(generated_input, opcode_keys, pseudo_code):
1963
  while i < len(lines):
1964
  raw_line = lines[i]
1965
  stripped_line = raw_line.strip()
1966
- print("Stripped line here -----1---->", stripped_line)
1967
- # Skip empty lines and comments
1968
  if not stripped_line or stripped_line.startswith("//"):
1969
  i += 1
1970
  continue
1971
 
1972
  current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2
1973
 
1974
- # Handle 'else' and 'end' first, as they control scope
1975
  if stripped_line.lower() == "else":
1976
  # Pop the 'then' substack's scope
1977
  popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop()
1978
  if popped_last_block_in_chain:
1979
  all_generated_blocks[popped_last_block_in_chain]["next"] = None
1980
 
1981
- # The 'if-else' block (popped_owner_key) is the owner.
1982
- # This 'else' must belong to the current owner on top of the stack (which should be the if-else block)
1983
- # Ensure the current_owner_block_id is indeed an if-else block
1984
- if popped_owner_key and all_generated_blocks[popped_owner_key]["op_code"] == "control_if_else":
 
 
 
 
 
1985
  # Push a new scope for the 'else' substack, with the same owner.
1986
- stack.append((current_indent, popped_owner_key, None)) # New scope for 'else' part, no last block yet
1987
  else:
1988
- # Error: 'else' found without a preceding 'if' or incorrect nesting
 
1989
  print(f"Error: 'else' found without a corresponding 'if-else' block at line {i+1}")
1990
- # Attempt to recover by treating it as a regular block or skipping
1991
- stack.append((popped_indent, popped_owner_key, popped_last_block_in_chain)) # Put back what was popped
1992
  i += 1
1993
  continue
1994
 
1995
  if stripped_line.lower() == "end":
1996
- # Pop the current substack's scope
1997
  popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop()
1998
  if popped_last_block_in_chain:
1999
- print("popped_last_block_in_chain is executed here")
2000
  all_generated_blocks[popped_last_block_in_chain]["next"] = None
2001
 
2002
- # If the popped scope had an owner (C-block or procedure definition),
2003
- # and its substack input isn't already filled, set it to None (empty substack).
2004
  if popped_owner_key:
2005
  owner_block = all_generated_blocks[popped_owner_key]
2006
  if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition":
2007
- # Determine which substack this 'end' is closing
2008
- # This logic needs to be smarter for if-else
2009
  if owner_block["op_code"] == "control_if_else":
2010
- # If we just popped the 'then' branch (SUBSTACK)
2011
- # and SUBSTACK2 is not yet set, then this 'end' closes SUBSTACK.
2012
- # If SUBSTACK2 was already set, this 'end' closes SUBSTACK2.
2013
- # If neither is filled, it's an empty 'then' branch.
2014
- if owner_block["inputs"].get("SUBSTACK") and owner_block["inputs"]["SUBSTACK"][1] is not None and \
2015
- (not owner_block["inputs"].get("SUBSTACK2") or owner_block["inputs"]["SUBSTACK2"][1] is None):
2016
- # This 'end' closes the first substack (then branch)
2017
- pass # Already handled by the stack logic
2018
- elif owner_block["inputs"].get("SUBSTACK2") and owner_block["inputs"]["SUBSTACK2"][1] is not None:
2019
- # This 'end' closes the second substack (else branch)
2020
- pass # Already handled by the stack logic
2021
- else: # Neither substack was filled, meaning an empty 'then' branch
2022
- # For now, ensure it's not set to a block ID if it was empty.
2023
- if "SUBSTACK" in owner_block["inputs"] and owner_block["inputs"]["SUBSTACK"][1] is None:
2024
- pass # Already None
2025
- if "SUBSTACK2" in owner_block["inputs"] and owner_block["inputs"]["SUBSTACK2"][1] is None:
2026
- pass # Already None
2027
- elif owner_block["inputs"].get("SUBSTACK") and owner_block["inputs"]["SUBSTACK"][1] is None: # Only set if not already set by a block
2028
- owner_block["inputs"]["SUBSTACK"] = [2, None] # No blocks in substack
2029
  i += 1
2030
  continue
2031
 
2032
- # Adjust stack based on indentation for regular blocks
2033
- # Pop scopes whose indentation is greater than or equal to the current line's indentation
2034
  while len(stack) > 1 and stack[-1][0] >= current_indent:
2035
  popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop()
2036
  if popped_last_block_in_chain:
2037
- all_generated_blocks[popped_last_block_in_chain]["next"] = None # Terminate the chain
2038
 
2039
- # Get the current active scope from the stack
2040
  current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1]
2041
 
2042
- # Classify the statement and create the block
2043
- #stmt_for_parse = stripped_line.rstrip("then").strip()
2044
- stmt_for_parse = re.sub(r"\bthen\s*$", "", stripped_line, flags=re.IGNORECASE).strip()
2045
- print("befor the classify hit here: ",stmt_for_parse)
2046
- opcode, ntype = classify(stmt_for_parse)
2047
- print("the opcode classify: ",opcode)
2048
- if opcode is None: # Should not happen if classify is robust
2049
  i += 1
2050
  continue
2051
 
2052
- # Create the new block (and register it in all_generated_blocks)
2053
- # _register_block now only sets parent for shadow/input blocks; main block parent/next/topLevel set here.
2054
  key = pick_key(opcode)
2055
- # Ensure the block exists in all_generated_blocks before trying to access it
 
2056
  if key not in all_generated_blocks:
2057
- # If not pre-generated, create a basic entry. This is a fallback.
2058
- all_generated_blocks[key] = copy.deepcopy(all_block_definitions.get(opcode, {}))
2059
  all_generated_blocks[key]["id"] = key
2060
  all_generated_blocks[key]["inputs"] = all_generated_blocks[key].get("inputs", {})
2061
  all_generated_blocks[key]["fields"] = all_generated_blocks[key].get("fields", {})
2062
- # Removed sub_stacks from here
2063
 
2064
  info = all_generated_blocks[key]
2065
-
2066
- # Set parent, next, and topLevel for the main script blocks
2067
  if ntype == "hat":
2068
  info["parent"] = None
2069
  info["topLevel"] = True
2070
  top_level_script_keys.append(key)
2071
- # Push a new scope for the children of this hat block.
2072
- stack.append((current_indent, key, None)) # New scope: owner is this hat, no last block yet
2073
- else: # Stack block or C-block (that is part of a linear sequence)
2074
  if last_block_in_current_chain:
2075
- # This block's parent is the previous block in the chain
2076
  info["parent"] = last_block_in_current_chain
2077
  all_generated_blocks[last_block_in_current_chain]["next"] = key
2078
  else:
2079
- # This is the first block in a new linear chain (e.g., first block inside a forever loop)
2080
- # Its parent is the owner of the current scope (the C-block or Hat block)
2081
  info["parent"] = current_owner_block_id
2082
 
2083
- # If the owner is a C-block or procedure definition, link its SUBSTACK input
2084
  if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"):
2085
  owner_block = all_generated_blocks[current_owner_block_id]
2086
  if owner_block["op_code"] == "control_if_else":
2087
- # If SUBSTACK is already set, this means we are starting SUBSTACK2 (else part)
2088
  if owner_block["inputs"].get("SUBSTACK") and owner_block["inputs"]["SUBSTACK"][1] is not None and \
2089
  (not owner_block["inputs"].get("SUBSTACK2") or owner_block["inputs"]["SUBSTACK2"][1] is None):
2090
  owner_block["inputs"]["SUBSTACK2"] = [2, key]
2091
- else: # This should be the first substack (then part)
2092
  owner_block["inputs"]["SUBSTACK"] = [2, key]
2093
- elif not owner_block["inputs"].get("SUBSTACK") or owner_block["inputs"]["SUBSTACK"][1] is None: # Only set if not already set by a block
2094
  owner_block["inputs"]["SUBSTACK"] = [2, key]
2095
  elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block":
2096
- # If the owner is a Hat block, this is its first child
2097
  all_generated_blocks[current_owner_block_id]["next"] = key
2098
 
2099
  info["topLevel"] = False
2100
- info["next"] = None # Default, will be overwritten if there's a next block
2101
 
2102
- # If it's a C-block or define block, it also starts a new inner scope
2103
  if ntype == "c_block" or opcode == "procedures_definition":
2104
- # Update the current scope's last_block_in_current_chain to this C-block
2105
  stack[-1] = (current_scope_indent, current_owner_block_id, key)
2106
- # Push a new scope for the C-block's substack
2107
- stack.append((current_indent, key, None)) # New scope: owner is this C-block, no last block yet
2108
  else:
2109
- # For regular stack blocks, just update the last_block_in_current_chain for the current scope
2110
  stack[-1] = (current_scope_indent, current_owner_block_id, key)
2111
 
2112
  # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition
@@ -2554,8 +2508,8 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
2554
 
2555
  # Initialize dictionaries to store and reuse generated unique IDs
2556
  # This prevents creating multiple unique IDs for the same variable/broadcast across different blocks
2557
- variable_id_map = defaultdict(lambda: generate_secure_token())
2558
- broadcast_id_map = defaultdict(lambda: generate_secure_token())
2559
 
2560
  # Define the mapping for input field names to their required integer types for shadows
2561
  input_type_mapping = {
@@ -2580,6 +2534,31 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
2580
  "VALUE_STRING": 10 # Used internally for VALUE when it should be type 10
2581
  }
2582
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2583
  for block_id, gen_block_data in generated_output_json.items():
2584
  processed_block = {}
2585
  all_gen_block_data = all_generated_blocks.get(block_id, {})
@@ -2595,13 +2574,14 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
2595
  if "mutation" in all_gen_block_data:
2596
  processed_block["mutation"] = all_gen_block_data["mutation"]
2597
 
 
 
2598
  # Process inputs
2599
  if "inputs" in all_gen_block_data:
2600
  for input_name, input_data in all_gen_block_data["inputs"].items():
 
2601
  if input_name == "BROADCAST_INPUT":
2602
- # Special handling for BROADCAST_INPUT (type 11)
2603
  if isinstance(input_data, dict) and input_data.get("kind") == "value":
2604
- # This is the new logic to handle a direct value for broadcast
2605
  menu_option = input_data.get("value", "message1")
2606
  broadcast_id = broadcast_id_map[menu_option]
2607
  processed_block["inputs"][input_name] = [
@@ -2613,23 +2593,26 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
2613
  ]
2614
  ]
2615
  elif isinstance(input_data, list) and len(input_data) == 2 and isinstance(input_data[1], list) and len(input_data[1]) == 3 and input_data[1][0] == 11:
2616
- # Preserve existing well-formed type 11 structure
2617
  processed_block["inputs"][input_name] = input_data
2618
  else:
2619
- # Fallback for unexpected formats, try to use the original if possible
2620
- processed_block["inputs"][input_name] = gen_block_data["inputs"].get(input_name, [1, [11, "message1", generate_secure_token()]])
2621
-
2622
- elif isinstance(input_data, dict):
2623
- if input_data.get("kind") == "value":
 
 
 
 
 
2624
  # Determine the correct shadow type based on the input_name and opcode
2625
- shadow_type = input_type_mapping.get(input_name, 4) # Default to 4
2626
-
2627
- # Specific override for "NUM" in "looks_goforwardbackwardlayers"
2628
- if input_name == "NUM" and processed_block["opcode"] == "looks_goforwardbackwardlayers":
2629
  shadow_type = 7
2630
-
2631
- # Specific override for "VALUE" in "data_setvariableto"
2632
- if input_name == "VALUE" and processed_block["opcode"] == "data_setvariableto":
2633
  shadow_type = 10
2634
 
2635
  processed_block["inputs"][input_name] = [
@@ -2639,119 +2622,142 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
2639
  str(input_data.get("value", ""))
2640
  ]
2641
  ]
2642
- elif input_data.get("kind") == "block":
2643
- # Case 3: Nested block input
 
 
2644
  nested_block_id = input_data.get("block", "")
2645
-
2646
  if nested_block_id in all_generated_blocks:
 
 
 
 
 
2647
  nested_block_opcode = all_generated_blocks[nested_block_id].get("op_code")
2648
-
2649
  if input_name in ["SUBSTACK", "CONDITION", "SUBSTACK2"]:
2650
- # These are control flow blocks, should always be type 2
2651
  processed_block["inputs"][input_name] = [2, nested_block_id]
2652
  elif nested_block_opcode in ["data_variable", "data_listcontents"]:
2653
- # It's a variable/list reporter, should be type 12 inside type 1
2654
  variable_name = all_generated_blocks[nested_block_id].get("fields", {}).get("VARIABLE", ["", ""])[0]
2655
  variable_id = all_generated_blocks[nested_block_id].get("fields", {}).get("VARIABLE", ["", ""])[1]
2656
  if not variable_id:
2657
  variable_id = variable_id_map[variable_name]
2658
  processed_block["inputs"][input_name] = [1, [12, variable_name, variable_id]]
2659
  elif input_name in ["TOUCHINGOBJECTMENU", "TO", "TOWARDS", "CLONE_OPTION", "SOUND_MENU", "KEY_OPTION", "OBJECT"]:
2660
- # These are menu/dropdown inputs that refer to another block/menu,
2661
- # and should be type 1, referring to a shadow block.
2662
  processed_block["inputs"][input_name] = [1, nested_block_id]
2663
  else:
2664
- # For other reporter blocks (like motion_xposition, operators),
2665
- # they should be type 3 when nested.
2666
- shadow_type_for_nested = input_type_mapping.get(input_name, 10) # Default to 10 for nested shadows
2667
- processed_block["inputs"][input_name] = [3, nested_block_id, [shadow_type_for_nested, ""]] # Empty shadow value
2668
  else:
2669
- # If the referenced block doesn't exist, treat as a broken reference for now
2670
- # or try to use original if available.
2671
- processed_block["inputs"][input_name] = gen_block_data["inputs"].get(input_name, [3, nested_block_id, [10, ""]]) # Fallback
2672
-
2673
- elif input_data.get("kind") == "menu":
2674
- # This handles menu inputs from all_gen_block_data that are not BROADCAST_INPUT
2675
- menu_option = input_data.get("option", "")
2676
- # These should typically be type 1 referring to a shadow block, or a direct value if no shadow block
2677
- # For simplicity, we'll try to get the block ID if it's a menu block.
2678
  if "block" in input_data:
2679
- processed_block["inputs"][input_name] = [1, input_data["block"]]
 
 
 
 
2680
  else:
2681
- # If it's a menu without a block reference, it might be a direct value in a shadow.
2682
- # This case is less common for menu 'kind'.
2683
- processed_block["inputs"][input_name] = [1, [10, menu_option]] # Default to type 10 for string menu options
2684
-
2685
-
2686
- elif isinstance(input_data, list):
2687
- # This branch handles existing list structures from all_gen_block_data (or generated_output_json if no all_gen_block_data)
2688
- # It needs to decide if it's a direct value, a block reference, or a special type (11, 12)
2689
 
 
 
 
 
2690
  if len(input_data) == 2:
2691
  first_val = input_data[0]
2692
  second_val = input_data[1]
2693
 
 
2694
  if isinstance(second_val, str) and second_val in all_generated_blocks:
2695
- # The second element is a block ID
 
 
 
 
2696
  nested_block_opcode = all_generated_blocks[second_val].get("op_code")
2697
 
2698
  if input_name in ["SUBSTACK", "CONDITION", "SUBSTACK2"]:
2699
- # These are control flow blocks, should always be type 2
2700
  processed_block["inputs"][input_name] = [2, second_val]
2701
  elif nested_block_opcode in ["data_variable", "data_listcontents"]:
2702
- # It's a variable/list reporter, should be type 12 inside type 1
2703
  variable_name = all_generated_blocks[second_val].get("fields", {}).get("VARIABLE", ["", ""])[0]
2704
  variable_id = all_generated_blocks[second_val].get("fields", {}).get("VARIABLE", ["", ""])[1]
2705
  if not variable_id:
2706
  variable_id = variable_id_map[variable_name]
2707
  processed_block["inputs"][input_name] = [1, [12, variable_name, variable_id]]
2708
  elif input_name in ["TOUCHINGOBJECTMENU", "TO", "TOWARDS", "CLONE_OPTION", "SOUND_MENU", "KEY_OPTION", "OBJECT"]:
2709
- # These are menu/dropdown inputs that refer to another block/menu,
2710
- # and should be type 1, referring to a shadow block.
2711
  processed_block["inputs"][input_name] = [1, second_val]
2712
  else:
2713
- # For other reporter blocks (like motion_xposition, operators),
2714
- # they should be type 3 when nested. Correct the first_val if it's not 3.
2715
  shadow_type_for_nested = input_type_mapping.get(input_name, 10)
 
2716
  processed_block["inputs"][input_name] = [3, second_val, [shadow_type_for_nested, ""]]
 
2717
 
2718
- elif isinstance(second_val, str):
2719
- # It's a value literal (e.g., "-240") that needs wrapping with a shadow type
2720
- shadow_type = input_type_mapping.get(input_name, 4) # Default to 4
2721
- if input_name == "NUM" and processed_block["opcode"] == "looks_goforwardbackwardlayers":
2722
  shadow_type = 7
2723
- if input_name == "VALUE" and processed_block["opcode"] == "data_setvariableto":
2724
  shadow_type = 10
2725
  processed_block["inputs"][input_name] = [1, [shadow_type, str(second_val)]]
2726
- else:
2727
- # Fallback for other 2-element lists (e.g., [1, [11, ...]]), copy as is
2728
- processed_block["inputs"][input_name] = input_data
 
 
2729
 
2730
  elif len(input_data) == 3 and isinstance(input_data[1], str) and isinstance(input_data[2], list):
2731
- # Case: [3, "block_id", [shadow_type, shadow_value]] - an already well-formed nested block
2732
- # or [1, "block_id", [shadow_type, shadow_value]] - an existing shadow for a direct value
2733
- # Preserve these if they conform to the rules, otherwise correct shadow type
2734
- if input_data[0] == 3 and input_data[1] in all_generated_blocks:
2735
- # This is a valid type 3 nested block, keep it as is
2736
- processed_block["inputs"][input_name] = input_data
2737
- elif input_data[0] == 1 and isinstance(input_data[1], list) and input_data[1][0] in [11, 12]:
2738
- # This is a type 11 or 12 structure, preserve it
2739
- processed_block["inputs"][input_name] = input_data
2740
- elif input_data[0] == 1 and isinstance(input_data[1], list) and len(input_data[1]) == 2:
2741
- # This is a type 1 with a shadow, re-evaluate shadow type
2742
- shadow_type = input_type_mapping.get(input_name, 4)
2743
- if input_name == "NUM" and processed_block["opcode"] == "looks_goforwardbackwardlayers":
2744
- shadow_type = 7
2745
- if input_name == "VALUE" and processed_block["opcode"] == "data_setvariableto":
2746
- shadow_type = 10
2747
- processed_block["inputs"][input_name] = [input_data[0], [shadow_type, str(input_data[1][1])]]
2748
- else:
2749
- # Fallback for other 3-element lists, copy as is
2750
- processed_block["inputs"][input_name] = input_data
 
 
 
 
 
 
 
 
 
 
 
 
 
2751
  else:
2752
- # Fallback for any other unexpected list formats, copy as is
2753
  processed_block["inputs"][input_name] = input_data
 
2754
 
 
 
2755
 
2756
  # Process fields
2757
  if "fields" in all_gen_block_data:
@@ -2797,7 +2803,7 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
2797
 
2798
  # Check if TOUCHINGOBJECTMENU input exists and is a block reference
2799
  referenced_block_id = None
2800
- if "TOUCHINGOBJECTMENU" in all_gen_block_data["inputs"]:
2801
  input_val = all_gen_block_data["inputs"]["TOUCHINGOBJECTMENU"]
2802
  if isinstance(input_val, list) and len(input_val) > 1 and isinstance(input_val[1], str):
2803
  referenced_block_id = input_val[1]
@@ -2815,7 +2821,7 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
2815
  else:
2816
  processed_block["fields"][field_name] = field_value
2817
 
2818
- # Remove unwanted keys from the processed block
2819
  keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"]
2820
  for key in keys_to_remove:
2821
  if key in processed_block:
@@ -2883,53 +2889,7 @@ def rename_blocks(block_json: Dict[str, Any], opcode_count: Dict[str, list]) ->
2883
  new_opcode_count[opcode] = [token_map.get(k, k) for k in key_list]
2884
 
2885
  return new_block_json, new_opcode_count
2886
- """
2887
- Replace each block key in block_json and each identifier in opcode_count
2888
- with a newly generated secure token.
2889
-
2890
- Args:
2891
- block_json: Mapping of block_key -> block_data.
2892
- opcode_count: Mapping of opcode -> list of block_keys.
2893
-
2894
- Returns:
2895
- A tuple of (new_block_json, new_opcode_count) with updated keys.
2896
- """
2897
- # Step 1: Generate a secure token mapping for every existing block key
2898
- token_map = {}
2899
- for old_key in block_json.keys():
2900
- # Ensure uniqueness in the unlikely event of a collision
2901
- while True:
2902
- new_key = generate_secure_token()
2903
- if new_key not in token_map.values():
2904
- break
2905
- token_map[old_key] = new_key
2906
-
2907
- # Step 2: Rebuild block_json with new keys
2908
- new_block_json = {}
2909
- for old_key, block in block_json.items():
2910
- new_key = token_map[old_key]
2911
- new_block_json[new_key] = block.copy()
2912
-
2913
- # Update parent and next references
2914
- if 'parent' in block and block['parent'] in token_map:
2915
- new_block_json[new_key]['parent'] = token_map[block['parent']]
2916
- if 'next' in block and block['next'] in token_map:
2917
- new_block_json[new_key]['next'] = token_map[block['next']]
2918
-
2919
- # Update inputs if they reference blocks
2920
- for inp_key, inp_val in block.get('inputs', {}).items():
2921
- if isinstance(inp_val, list) and len(inp_val) == 2:
2922
- idx, ref = inp_val
2923
- if idx in (2, 3) and isinstance(ref, str) and ref in token_map:
2924
- new_block_json[new_key]['inputs'][inp_key] = [idx, token_map[ref]]
2925
-
2926
- # Step 3: Update opcode count map
2927
- new_opcode_count = {}
2928
- for opcode, key_list in opcode_count.items():
2929
- new_opcode_count[opcode] = [token_map.get(k, k) for k in key_list]
2930
-
2931
- return new_block_json, new_opcode_count
2932
-
2933
  #################################################################################################################################################################
2934
  #--------------------------------------------------[Helper function to add Variables and Broadcasts [USed in main app file for main projectjson]]----------------
2935
  #################################################################################################################################################################
 
260
  "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
261
  },
262
  "control_if_else": {
263
+ "block_name": "if <> then go else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else",
264
  "functionality": "Executes one set of blocks if the specified boolean condition is true, and a different set of blocks if the condition is false. [NOTE: it takes boolean blocks as input]",
265
  "inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
266
  },
 
1863
 
1864
  # Control Blocks
1865
  # if re.match(r"wait (.+?) seconds", l): return "control_wait", "stack"
1866
+ # if re.match(r"wait\s+(\(?\[?.+?\]?\)?)\s*(sec(?:ond)?s?)?$", l): return "control_wait", "stack"
1867
+ # if l.startswith("wait until <"): return "control_wait_until", "stack"
1868
+ if re.match(r"wait\s+(?!until)(\(?\[?.+?\]?\)?)\s*(sec(?:ond)?s?)?$", l, re.IGNORECASE): return "control_wait", "stack"
1869
+ if re.match(r"wait\s+until\s*<", l, re.IGNORECASE): return "control_wait_until", "stack"
1870
  if l.startswith("repeat ("): return "control_repeat", "c_block"
1871
  if l == "forever": return "control_forever", "c_block"
1872
+ if l.startswith("if <") and " then go" in l: return "control_if_else", "c_block"
1873
  if l.startswith("if <"): return "control_if", "c_block"
1874
  if l.startswith("repeat until <"): return "control_repeat_until", "c_block"
1875
  # Updated regex for stop block to handle different options
 
1926
  """
1927
  Build a nested “plan” tree from:
1928
  • generated_input: dict of block_key -> block_data (pre-generated block definitions)
1929
+ • opcode_keys: dict of opcode -> list of block_keys (in order)
1930
+ • pseudo_code: a multiline string, indented with two-space levels
1931
 
1932
  Returns:
1933
  { "flow": [ ... list of block dictionaries ... ] }
1934
  """
 
1935
  ptrs = defaultdict(int)
1936
  def pick_key(opcode):
1937
  lst = opcode_keys.get(opcode, [])
1938
  idx = ptrs[opcode]
1939
  if idx >= len(lst):
 
 
1940
  ptrs[opcode] += 1
1941
  return f"{opcode}_{idx + 1}"
1942
  ptrs[opcode] += 1
1943
  return lst[idx]
1944
 
1945
+ # Change: Start with an empty dictionary. Blocks will be added only as they are parsed.
1946
+ all_generated_blocks = {}
 
 
 
 
 
 
 
 
 
 
1947
 
1948
+ stack = [(-2, None, None)]
1949
  top_level_script_keys = []
1950
 
1951
  lines = pseudo_code.splitlines()
 
1953
  while i < len(lines):
1954
  raw_line = lines[i]
1955
  stripped_line = raw_line.strip()
1956
+
 
1957
  if not stripped_line or stripped_line.startswith("//"):
1958
  i += 1
1959
  continue
1960
 
1961
  current_indent = (len(raw_line) - len(raw_line.lstrip())) // 2
1962
 
 
1963
  if stripped_line.lower() == "else":
1964
  # Pop the 'then' substack's scope
1965
  popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop()
1966
  if popped_last_block_in_chain:
1967
  all_generated_blocks[popped_last_block_in_chain]["next"] = None
1968
 
1969
+ # Check if the popped scope's owner is an IF block.
1970
+ # This check now looks for any C-Block, which is correct for this context.
1971
+ if popped_owner_key and all_generated_blocks[popped_owner_key]["block_shape"] == "C-Block":
1972
+
1973
+ # This is the crucial step that fixes the issue.
1974
+ # We explicitly upgrade the block from a 'control_if' to a 'control_if_else'.
1975
+ owner_block = all_generated_blocks[popped_owner_key]
1976
+ owner_block["op_code"] = "control_if_else"
1977
+
1978
  # Push a new scope for the 'else' substack, with the same owner.
1979
+ stack.append((current_indent, popped_owner_key, None))
1980
  else:
1981
+ # This is the error you were getting.
1982
+ # It happens because the 'if' block was never correctly upgraded.
1983
  print(f"Error: 'else' found without a corresponding 'if-else' block at line {i+1}")
1984
+ stack.append((popped_indent, popped_owner_key, popped_last_block_in_chain))
 
1985
  i += 1
1986
  continue
1987
 
1988
  if stripped_line.lower() == "end":
 
1989
  popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop()
1990
  if popped_last_block_in_chain:
 
1991
  all_generated_blocks[popped_last_block_in_chain]["next"] = None
1992
 
 
 
1993
  if popped_owner_key:
1994
  owner_block = all_generated_blocks[popped_owner_key]
1995
  if owner_block["block_shape"] == "C-Block" or owner_block["op_code"] == "procedures_definition":
 
 
1996
  if owner_block["op_code"] == "control_if_else":
1997
+ if owner_block["inputs"].get("SUBSTACK2") and owner_block["inputs"]["SUBSTACK2"][1] is not None:
1998
+ pass
1999
+ else:
2000
+ pass
2001
+ elif owner_block["inputs"].get("SUBSTACK") and owner_block["inputs"]["SUBSTACK"][1] is None:
2002
+ owner_block["inputs"]["SUBSTACK"] = [2, None]
 
 
 
 
 
 
 
 
 
 
 
 
 
2003
  i += 1
2004
  continue
2005
 
 
 
2006
  while len(stack) > 1 and stack[-1][0] >= current_indent:
2007
  popped_indent, popped_owner_key, popped_last_block_in_chain = stack.pop()
2008
  if popped_last_block_in_chain:
2009
+ all_generated_blocks[popped_last_block_in_chain]["next"] = None
2010
 
 
2011
  current_scope_indent, current_owner_block_id, last_block_in_current_chain = stack[-1]
2012
 
2013
+ opcode, ntype = classify(stripped_line)
2014
+ stmt_for_parse = re.sub(r"\bthen(\s+go)?\s*$", "", stripped_line, flags=re.IGNORECASE).strip()
2015
+ print("The opcode here is",opcode)
2016
+ print("The ntype here is",ntype)
2017
+ if opcode is None:
 
 
2018
  i += 1
2019
  continue
2020
 
 
 
2021
  key = pick_key(opcode)
2022
+
2023
+ # Change: The code now relies on this check to create a block only if it's new.
2024
  if key not in all_generated_blocks:
2025
+ all_generated_blocks[key] = copy.deepcopy(generated_input.get(key, all_block_definitions.get(opcode, {})))
 
2026
  all_generated_blocks[key]["id"] = key
2027
  all_generated_blocks[key]["inputs"] = all_generated_blocks[key].get("inputs", {})
2028
  all_generated_blocks[key]["fields"] = all_generated_blocks[key].get("fields", {})
 
2029
 
2030
  info = all_generated_blocks[key]
2031
+
 
2032
  if ntype == "hat":
2033
  info["parent"] = None
2034
  info["topLevel"] = True
2035
  top_level_script_keys.append(key)
2036
+ stack.append((current_indent, key, None))
2037
+ else:
 
2038
  if last_block_in_current_chain:
 
2039
  info["parent"] = last_block_in_current_chain
2040
  all_generated_blocks[last_block_in_current_chain]["next"] = key
2041
  else:
 
 
2042
  info["parent"] = current_owner_block_id
2043
 
 
2044
  if current_owner_block_id and (all_generated_blocks[current_owner_block_id]["block_shape"] == "C-Block" or all_generated_blocks[current_owner_block_id]["op_code"] == "procedures_definition"):
2045
  owner_block = all_generated_blocks[current_owner_block_id]
2046
  if owner_block["op_code"] == "control_if_else":
 
2047
  if owner_block["inputs"].get("SUBSTACK") and owner_block["inputs"]["SUBSTACK"][1] is not None and \
2048
  (not owner_block["inputs"].get("SUBSTACK2") or owner_block["inputs"]["SUBSTACK2"][1] is None):
2049
  owner_block["inputs"]["SUBSTACK2"] = [2, key]
2050
+ else:
2051
  owner_block["inputs"]["SUBSTACK"] = [2, key]
2052
+ elif not owner_block["inputs"].get("SUBSTACK") or owner_block["inputs"]["SUBSTACK"][1] is None:
2053
  owner_block["inputs"]["SUBSTACK"] = [2, key]
2054
  elif current_owner_block_id and all_generated_blocks[current_owner_block_id]["block_shape"] == "Hat Block":
 
2055
  all_generated_blocks[current_owner_block_id]["next"] = key
2056
 
2057
  info["topLevel"] = False
2058
+ info["next"] = None
2059
 
 
2060
  if ntype == "c_block" or opcode == "procedures_definition":
 
2061
  stack[-1] = (current_scope_indent, current_owner_block_id, key)
2062
+ stack.append((current_indent, key, None))
 
2063
  else:
 
2064
  stack[-1] = (current_scope_indent, current_owner_block_id, key)
2065
 
2066
  # Parse inputs and fields (this part remains largely the same, but ensure parse_reporter_or_value/parse_condition
 
2508
 
2509
  # Initialize dictionaries to store and reuse generated unique IDs
2510
  # This prevents creating multiple unique IDs for the same variable/broadcast across different blocks
2511
+ variable_id_map = defaultdict(lambda: generate_secure_token(20))
2512
+ broadcast_id_map = defaultdict(lambda: generate_secure_token(20))
2513
 
2514
  # Define the mapping for input field names to their required integer types for shadows
2515
  input_type_mapping = {
 
2534
  "VALUE_STRING": 10 # Used internally for VALUE when it should be type 10
2535
  }
2536
 
2537
+ # Explicit mapping of opcodes -> expected menu inputs that MUST be simple menu/shadow links ([1, block_id])
2538
+ explicit_menu_links = {
2539
+ "motion_goto": [("TO", "motion_goto_menu")],
2540
+ "motion_glideto": [("TO", "motion_glideto_menu")],
2541
+ "motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")],
2542
+ "sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")],
2543
+ "sensing_of": [("OBJECT", "sensing_of_object_menu")],
2544
+ "sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")],
2545
+ "control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")],
2546
+ "sound_play": [("SOUND_MENU", "sound_sounds_menu")],
2547
+ "sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")],
2548
+ "looks_switchcostumeto": [("COSTUME", "looks_costume")],
2549
+ "looks_switchbackdropto": [("BACKDROP", "looks_backdrops")],
2550
+ }
2551
+
2552
+ # helper to check if this opcode+input_name is an explicit menu link
2553
+ def is_explicit_menu_opcode_input(opcode, input_name):
2554
+ if not opcode:
2555
+ return False
2556
+ pairs = explicit_menu_links.get(opcode, [])
2557
+ for inp, _menu in pairs:
2558
+ if inp == input_name:
2559
+ return True
2560
+ return False
2561
+
2562
  for block_id, gen_block_data in generated_output_json.items():
2563
  processed_block = {}
2564
  all_gen_block_data = all_generated_blocks.get(block_id, {})
 
2574
  if "mutation" in all_gen_block_data:
2575
  processed_block["mutation"] = all_gen_block_data["mutation"]
2576
 
2577
+ opcode = processed_block["opcode"] # convenience
2578
+
2579
  # Process inputs
2580
  if "inputs" in all_gen_block_data:
2581
  for input_name, input_data in all_gen_block_data["inputs"].items():
2582
+ # --- BROADCAST INPUT special handling (type 11) ---
2583
  if input_name == "BROADCAST_INPUT":
 
2584
  if isinstance(input_data, dict) and input_data.get("kind") == "value":
 
2585
  menu_option = input_data.get("value", "message1")
2586
  broadcast_id = broadcast_id_map[menu_option]
2587
  processed_block["inputs"][input_name] = [
 
2593
  ]
2594
  ]
2595
  elif isinstance(input_data, list) and len(input_data) == 2 and isinstance(input_data[1], list) and len(input_data[1]) == 3 and input_data[1][0] == 11:
2596
+ # Preserve well-formed existing type 11 structure
2597
  processed_block["inputs"][input_name] = input_data
2598
  else:
2599
+ # Fallback: try original generated_output_json value if present, else synthesize
2600
+ fallback = gen_block_data.get("inputs", {}).get(input_name,
2601
+ [1, [11, "message1", generate_secure_token(20)]])
2602
+ processed_block["inputs"][input_name] = fallback
2603
+ continue
2604
+
2605
+ # --- Input described as a dict (value, block, menu) ---
2606
+ if isinstance(input_data, dict):
2607
+ kind = input_data.get("kind")
2608
+ if kind == "value":
2609
  # Determine the correct shadow type based on the input_name and opcode
2610
+ shadow_type = input_type_mapping.get(input_name, 4) # default 4
2611
+
2612
+ # Specific overrides
2613
+ if input_name == "NUM" and opcode == "looks_goforwardbackwardlayers":
2614
  shadow_type = 7
2615
+ if input_name == "VALUE" and opcode == "data_setvariableto":
 
 
2616
  shadow_type = 10
2617
 
2618
  processed_block["inputs"][input_name] = [
 
2622
  str(input_data.get("value", ""))
2623
  ]
2624
  ]
2625
+ continue
2626
+
2627
+ if kind == "block":
2628
+ # nested block reference via dict
2629
  nested_block_id = input_data.get("block", "")
 
2630
  if nested_block_id in all_generated_blocks:
2631
+ # If this opcode+input_name is explicitly a menu -> force [1, nested_block_id]
2632
+ if is_explicit_menu_opcode_input(opcode, input_name):
2633
+ processed_block["inputs"][input_name] = [1, nested_block_id]
2634
+ continue
2635
+
2636
  nested_block_opcode = all_generated_blocks[nested_block_id].get("op_code")
2637
+
2638
  if input_name in ["SUBSTACK", "CONDITION", "SUBSTACK2"]:
 
2639
  processed_block["inputs"][input_name] = [2, nested_block_id]
2640
  elif nested_block_opcode in ["data_variable", "data_listcontents"]:
2641
+ # variable/list reporter inside a reporter -> [1, [12, name, id]]
2642
  variable_name = all_generated_blocks[nested_block_id].get("fields", {}).get("VARIABLE", ["", ""])[0]
2643
  variable_id = all_generated_blocks[nested_block_id].get("fields", {}).get("VARIABLE", ["", ""])[1]
2644
  if not variable_id:
2645
  variable_id = variable_id_map[variable_name]
2646
  processed_block["inputs"][input_name] = [1, [12, variable_name, variable_id]]
2647
  elif input_name in ["TOUCHINGOBJECTMENU", "TO", "TOWARDS", "CLONE_OPTION", "SOUND_MENU", "KEY_OPTION", "OBJECT"]:
2648
+ # menu/dropdown inputs that refer to another block/menu -> type 1, refer to block id
 
2649
  processed_block["inputs"][input_name] = [1, nested_block_id]
2650
  else:
2651
+ # other reporter blocks (like motion_xposition, operators) -> nested type 3
2652
+ shadow_type_for_nested = input_type_mapping.get(input_name, 10)
2653
+ processed_block["inputs"][input_name] = [3, nested_block_id, [shadow_type_for_nested, ""]]
 
2654
  else:
2655
+ # referenced block missing: fallback to original input format if available, else a safe fallback
2656
+ processed_block["inputs"][input_name] = gen_block_data.get("inputs", {}).get(input_name, [3, nested_block_id, [10, ""]])
2657
+ continue
2658
+
2659
+ if kind == "menu":
2660
+ # menu kind: either references a block or a direct value
 
 
 
2661
  if "block" in input_data:
2662
+ # If explicit menu, ensure we store as [1, block_id]
2663
+ if is_explicit_menu_opcode_input(opcode, input_name):
2664
+ processed_block["inputs"][input_name] = [1, input_data["block"]]
2665
+ else:
2666
+ processed_block["inputs"][input_name] = [1, input_data["block"]]
2667
  else:
2668
+ # direct value in menu -> type 1 with a string shadow (type 10)
2669
+ menu_option = input_data.get("option", "")
2670
+ processed_block["inputs"][input_name] = [1, [10, menu_option]]
2671
+ continue
 
 
 
 
2672
 
2673
+ # --- Input is a list already (various formats) ---
2674
+ if isinstance(input_data, list):
2675
+ # Common shapes:
2676
+ # [1, <value shadow>], [1, <block_id>], [3, <block_id>, [shadow_type, value]], etc.
2677
  if len(input_data) == 2:
2678
  first_val = input_data[0]
2679
  second_val = input_data[1]
2680
 
2681
+ # If the second element is a block id that exists
2682
  if isinstance(second_val, str) and second_val in all_generated_blocks:
2683
+ # If explicit menu mapping -> force [1, block_id]
2684
+ if is_explicit_menu_opcode_input(opcode, input_name):
2685
+ processed_block["inputs"][input_name] = [1, second_val]
2686
+ continue
2687
+
2688
  nested_block_opcode = all_generated_blocks[second_val].get("op_code")
2689
 
2690
  if input_name in ["SUBSTACK", "CONDITION", "SUBSTACK2"]:
 
2691
  processed_block["inputs"][input_name] = [2, second_val]
2692
  elif nested_block_opcode in ["data_variable", "data_listcontents"]:
 
2693
  variable_name = all_generated_blocks[second_val].get("fields", {}).get("VARIABLE", ["", ""])[0]
2694
  variable_id = all_generated_blocks[second_val].get("fields", {}).get("VARIABLE", ["", ""])[1]
2695
  if not variable_id:
2696
  variable_id = variable_id_map[variable_name]
2697
  processed_block["inputs"][input_name] = [1, [12, variable_name, variable_id]]
2698
  elif input_name in ["TOUCHINGOBJECTMENU", "TO", "TOWARDS", "CLONE_OPTION", "SOUND_MENU", "KEY_OPTION", "OBJECT"]:
 
 
2699
  processed_block["inputs"][input_name] = [1, second_val]
2700
  else:
 
 
2701
  shadow_type_for_nested = input_type_mapping.get(input_name, 10)
2702
+ # Ensure nested reporters use type 3
2703
  processed_block["inputs"][input_name] = [3, second_val, [shadow_type_for_nested, ""]]
2704
+ continue
2705
 
2706
+ # If the second element is a string literal value -> wrap with appropriate shadow type
2707
+ if isinstance(second_val, str):
2708
+ shadow_type = input_type_mapping.get(input_name, 4) # default 4
2709
+ if input_name == "NUM" and opcode == "looks_goforwardbackwardlayers":
2710
  shadow_type = 7
2711
+ if input_name == "VALUE" and opcode == "data_setvariableto":
2712
  shadow_type = 10
2713
  processed_block["inputs"][input_name] = [1, [shadow_type, str(second_val)]]
2714
+ continue
2715
+
2716
+ # fallback: preserve the input as-is
2717
+ processed_block["inputs"][input_name] = input_data
2718
+ continue
2719
 
2720
  elif len(input_data) == 3 and isinstance(input_data[1], str) and isinstance(input_data[2], list):
2721
+ # case: [3, "block_id", [shadow_type, shadow_value]] or
2722
+ # [1, "block_id", [shadow_type, shadow_value]] (rare)
2723
+ block_ref = input_data[1]
2724
+ if block_ref in all_generated_blocks:
2725
+ # If explicit menu mapping -> convert to [1, block_id]
2726
+ if is_explicit_menu_opcode_input(opcode, input_name):
2727
+ processed_block["inputs"][input_name] = [1, block_ref]
2728
+ continue
2729
+
2730
+ # keep already well-formed nested reporter [3, ...]
2731
+ if input_data[0] == 3:
2732
+ processed_block["inputs"][input_name] = input_data
2733
+ continue
2734
+
2735
+ # If it's a type 1 with a type shadow like [1, [11,...]] or [1, [12,...]] -> preserve
2736
+ if input_data[0] == 1 and isinstance(input_data[1], list) and input_data[1][0] in [11, 12]:
2737
+ processed_block["inputs"][input_name] = input_data
2738
+ continue
2739
+
2740
+ # if it's [1, [shadow_type, value]] then re-evaluate shadow type
2741
+ if input_data[0] == 1 and isinstance(input_data[1], list) and len(input_data[1]) == 2:
2742
+ shadow_type = input_type_mapping.get(input_name, 4)
2743
+ if input_name == "NUM" and opcode == "looks_goforwardbackwardlayers":
2744
+ shadow_type = 7
2745
+ if input_name == "VALUE" and opcode == "data_setvariableto":
2746
+ shadow_type = 10
2747
+ processed_block["inputs"][input_name] = [input_data[0], [shadow_type, str(input_data[1][1])]]
2748
+ continue
2749
+
2750
+ # fallback: preserve the list as-is
2751
+ processed_block["inputs"][input_name] = input_data
2752
+ continue
2753
+
2754
  else:
2755
+ # other unexpected list shapes -> preserve for now
2756
  processed_block["inputs"][input_name] = input_data
2757
+ continue
2758
 
2759
+ # If input_data didn't match any of the above -> try to fallback to original generated value if available
2760
+ processed_block["inputs"][input_name] = gen_block_data.get("inputs", {}).get(input_name, input_data)
2761
 
2762
  # Process fields
2763
  if "fields" in all_gen_block_data:
 
2803
 
2804
  # Check if TOUCHINGOBJECTMENU input exists and is a block reference
2805
  referenced_block_id = None
2806
+ if "TOUCHINGOBJECTMENU" in all_gen_block_data.get("inputs", {}):
2807
  input_val = all_gen_block_data["inputs"]["TOUCHINGOBJECTMENU"]
2808
  if isinstance(input_val, list) and len(input_val) > 1 and isinstance(input_val[1], str):
2809
  referenced_block_id = input_val[1]
 
2821
  else:
2822
  processed_block["fields"][field_name] = field_value
2823
 
2824
+ # Remove unwanted keys from the processed block (if somehow present)
2825
  keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"]
2826
  for key in keys_to_remove:
2827
  if key in processed_block:
 
2889
  new_opcode_count[opcode] = [token_map.get(k, k) for k in key_list]
2890
 
2891
  return new_block_json, new_opcode_count
2892
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2893
  #################################################################################################################################################################
2894
  #--------------------------------------------------[Helper function to add Variables and Broadcasts [USed in main app file for main projectjson]]----------------
2895
  #################################################################################################################################################################