mirror of
https://github.com/AnxiousAnt/PatchGen.git
synced 2026-04-01 11:49:38 +02:00
190 lines
6.0 KiB
Python
190 lines
6.0 KiB
Python
def read_pd_file(file_path):
|
|
"""
|
|
Reads a .pd file and splits it into individual records.
|
|
|
|
Parameters:
|
|
- file_path (str): Path to the .pd file.
|
|
|
|
Returns:
|
|
- records (list): List of records, where each record is a dictionary with
|
|
'chunk_type', 'element_type', 'parameters', and 'original_line'.
|
|
"""
|
|
records = []
|
|
|
|
with open(file_path, 'r') as file:
|
|
for i, line in enumerate(file):
|
|
# Strip the line endings
|
|
line = line.strip()
|
|
|
|
# Skip empty lines
|
|
if not line:
|
|
continue
|
|
|
|
# Check if the line ends with a semicolon and starts with '#'
|
|
if not line.endswith(';') or not line.startswith('#'):
|
|
records.append({
|
|
'chunk_type': None,
|
|
'element_type': None,
|
|
'parameters': None,
|
|
'original_line': line,
|
|
'index': i,
|
|
'error': f"Syntax error: Line does not start with '#' or end with ';': {line}"
|
|
})
|
|
continue
|
|
|
|
# Remove the leading '#' and trailing ';'
|
|
line_content = line[1:-1].strip()
|
|
|
|
# Split the line into parts
|
|
parts = line_content.split()
|
|
|
|
if len(parts) < 2:
|
|
records.append({
|
|
'chunk_type': None,
|
|
'element_type': None,
|
|
'parameters': None,
|
|
'original_line': line,
|
|
'index': i,
|
|
'error': f"Syntax error: Insufficient parts in the line: {line}"
|
|
})
|
|
continue
|
|
|
|
# Extract chunk type and element type
|
|
chunk_type = parts[0]
|
|
element_type = parts[1]
|
|
parameters = parts[2:]
|
|
|
|
# Append to the records list
|
|
records.append({
|
|
'chunk_type': chunk_type,
|
|
'element_type': element_type,
|
|
'parameters': parameters,
|
|
'original_line': line,
|
|
'index': i,
|
|
'error': None # No error initially
|
|
})
|
|
|
|
return records
|
|
|
|
def validate_canvas(parameters):
|
|
errors = []
|
|
if len(parameters) != 6:
|
|
errors.append("Canvas element must have exactly 6 parameters.")
|
|
try:
|
|
int(parameters[0])
|
|
int(parameters[1])
|
|
int(parameters[2])
|
|
int(parameters[3])
|
|
except ValueError:
|
|
errors.append("Canvas positions and sizes must be integers.")
|
|
try:
|
|
int(parameters[5])
|
|
except ValueError:
|
|
errors.append("Canvas 'open_on_load' flag must be an integer (0 or 1).")
|
|
return errors
|
|
|
|
def validate_connect(parameters):
|
|
errors = []
|
|
if len(parameters) != 4:
|
|
errors.append("Connect element must have exactly 4 parameters.")
|
|
try:
|
|
int(parameters[0])
|
|
int(parameters[1])
|
|
int(parameters[2])
|
|
int(parameters[3])
|
|
except ValueError:
|
|
errors.append("Connect parameters must be integers.")
|
|
return errors
|
|
|
|
def validate_obj(parameters):
|
|
errors = []
|
|
if len(parameters) < 3:
|
|
errors.append("Object element must have at least 3 parameters.")
|
|
return errors
|
|
|
|
def validate_msg(parameters):
|
|
errors = []
|
|
if len(parameters) < 2:
|
|
errors.append("Message element must have at least 2 parameters.")
|
|
return errors
|
|
|
|
def validate_pd_records(records):
|
|
errors = []
|
|
for record in records:
|
|
if record['error'] is not None:
|
|
errors.append(record)
|
|
continue
|
|
chunk_type = record['chunk_type']
|
|
element_type = record['element_type']
|
|
parameters = record['parameters']
|
|
|
|
if chunk_type not in {"X", "N", "A"}:
|
|
record['error'] = f"Invalid chunk type '{chunk_type}'."
|
|
errors.append(record)
|
|
continue
|
|
|
|
if element_type == "canvas":
|
|
canvas_errors = validate_canvas(parameters)
|
|
if canvas_errors:
|
|
record['error'] = "; ".join(canvas_errors)
|
|
errors.append(record)
|
|
elif element_type == "connect":
|
|
connect_errors = validate_connect(parameters)
|
|
if connect_errors:
|
|
record['error'] = "; ".join(connect_errors)
|
|
errors.append(record)
|
|
elif element_type == "obj":
|
|
obj_errors = validate_obj(parameters)
|
|
if obj_errors:
|
|
record['error'] = "; ".join(obj_errors)
|
|
errors.append(record)
|
|
elif element_type == "msg":
|
|
msg_errors = validate_msg(parameters)
|
|
if msg_errors:
|
|
record['error'] = "; ".join(msg_errors)
|
|
errors.append(record)
|
|
else:
|
|
# Add validation for other element types as needed
|
|
pass
|
|
|
|
return errors
|
|
|
|
def display_with_errors(file_path, records, errors):
|
|
with open(file_path, 'r') as file:
|
|
original_lines = file.readlines()
|
|
|
|
error_indices = {record['index']: record['error'] for record in errors}
|
|
|
|
print("Original Patch:")
|
|
for i, line in enumerate(original_lines):
|
|
print(f"{i}: {line.strip()}")
|
|
|
|
print("\nPatch with Errors Highlighted:")
|
|
for i, record in enumerate(records):
|
|
print(f"{record['index']}: {record['original_line'].strip()}")
|
|
if record['index'] in error_indices:
|
|
print(f" >> Error: {error_indices[record['index']]}")
|
|
|
|
# Writing the sample file content to 'sample.pd'
|
|
sample_pd_content = """
|
|
#N canvas 0 0 450 300 sample_patch 1;
|
|
#N canvas taes err
|
|
#X obj 50 50 osc~ 440;
|
|
#X obj 50 100 dac~;
|
|
#X connect 0 0 1 0;
|
|
#X connect 0 1 1;
|
|
"""
|
|
|
|
with open('sample.pd', 'w') as file:
|
|
file.write(sample_pd_content.strip())
|
|
|
|
# Reading and validating the sample file
|
|
file_path = 'sample.pd'
|
|
records = read_pd_file(file_path)
|
|
errors = validate_pd_records(records)
|
|
|
|
if errors:
|
|
display_with_errors(file_path, records, errors)
|
|
else:
|
|
print("No syntax errors found.")
|