parsers.py
: Simplify if/elses in split_tja_lines_into_course()
Some highlights: - Strip comments ahead of time - _Only_ parse #START in split_tja_lines_into_course() - Leave all other parsing to parse_tja_course_data() This makes the function behavior line up with what the docstring suggests, and removes some convoluted parsing in split_tja_lines_into_course().
This commit is contained in:
parent
fc1b0d1bbc
commit
24b5828833
@ -54,6 +54,10 @@ def split_tja_lines_into_courses(lines):
|
|||||||
The data for each TJACourse can then be parsed individually using the
|
The data for each TJACourse can then be parsed individually using the
|
||||||
`parse_tja_course_data()` function.
|
`parse_tja_course_data()` function.
|
||||||
"""
|
"""
|
||||||
|
# Strip leading/trailing whitespace and comments ('// Comment')
|
||||||
|
lines = [line.split("//")[0].strip() for line in lines
|
||||||
|
if line.split("//")[0].strip()]
|
||||||
|
|
||||||
parsed_tja = None
|
parsed_tja = None
|
||||||
current_course = ''
|
current_course = ''
|
||||||
current_course_cached = ''
|
current_course_cached = ''
|
||||||
@ -61,8 +65,11 @@ def split_tja_lines_into_courses(lines):
|
|||||||
song_offset = 0
|
song_offset = 0
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# Case 1: Metadata lines
|
# Only metadata and #START commands are relevant for this function
|
||||||
match_metadata = re.match(r"^([A-Z]+):(.*)", line)
|
match_metadata = re.match(r"^([A-Z]+):(.*)", line)
|
||||||
|
match_start = re.match(r"^#START(?:\s+(.+))?", line)
|
||||||
|
|
||||||
|
# Case 1: Metadata lines
|
||||||
if match_metadata:
|
if match_metadata:
|
||||||
name_upper = match_metadata.group(1).upper()
|
name_upper = match_metadata.group(1).upper()
|
||||||
value = match_metadata.group(2).strip()
|
value = match_metadata.group(2).strip()
|
||||||
@ -105,37 +112,27 @@ def split_tja_lines_into_courses(lines):
|
|||||||
else:
|
else:
|
||||||
pass # Ignore 'TITLE', 'SUBTITLE', 'WAVE', etc.
|
pass # Ignore 'TITLE', 'SUBTITLE', 'WAVE', etc.
|
||||||
|
|
||||||
# Case 2: Commands and note data (to be further processed
|
# Case 2: #START commands
|
||||||
# course-by-course later on)
|
elif match_start:
|
||||||
elif not re.match(r"//.*", line): # Exclude comment-only lines ('//')
|
value = match_start.group(1) if match_start.group(1) else ''
|
||||||
match_command = re.match(r"^#([A-Z]+)(?:\s+(.+))?", line)
|
# For STYLE:Double, #START P1/P2 indicates the start of a new
|
||||||
match_notes = re.match(r"^(([0-9]|A|B|C|F|G)*,?).*$", line)
|
# chart. But, we want multiplayer charts to inherit the
|
||||||
if match_command:
|
# metadata from the course as a whole, so we deepcopy the
|
||||||
name_upper = match_command.group(1).upper()
|
# existing course for that difficulty.
|
||||||
value = (match_command.group(2).strip()
|
if value in ["P1", "P2"]:
|
||||||
if match_command.group(2) else '')
|
current_course = current_course_cached + value
|
||||||
# For STYLE:Double, #START P1/P2 indicates the start of a new
|
parsed_tja.courses[current_course] = \
|
||||||
# chart. But, we want multiplayer charts to inherit the
|
deepcopy(parsed_tja.courses[current_course_cached])
|
||||||
# metadata from the course as a whole, so we deepcopy the
|
parsed_tja.courses[current_course].data = list()
|
||||||
# existing course for that difficulty.
|
elif value:
|
||||||
if name_upper == "START":
|
raise ValueError(f"Invalid value '{value}' for #START.")
|
||||||
if value in ["P1", "P2"]:
|
|
||||||
current_course = current_course_cached + value
|
# Since P1/P2 has been handled, we can just use a normal '#START'
|
||||||
parsed_tja.courses[current_course] = \
|
parsed_tja.courses[current_course].data.append("#START")
|
||||||
deepcopy(parsed_tja.courses[current_course_cached])
|
|
||||||
parsed_tja.courses[current_course].data = list()
|
# Case 3: For other commands and data, simply copy as-is (parse later)
|
||||||
# Once we've made the new course, we can reset
|
else:
|
||||||
# #START P1/P2 to a normal #START command
|
parsed_tja.courses[current_course].data.append(line)
|
||||||
value = ''
|
|
||||||
elif value:
|
|
||||||
raise ValueError(f"Invalid value '{value}' for "
|
|
||||||
f"#START command.")
|
|
||||||
elif match_notes:
|
|
||||||
name_upper = 'NOTES'
|
|
||||||
value = match_notes.group(1)
|
|
||||||
parsed_tja.courses[current_course].data.append(
|
|
||||||
TJAData(name_upper, value)
|
|
||||||
)
|
|
||||||
|
|
||||||
# If a course has no song data, then this is likely because the course has
|
# If a course has no song data, then this is likely because the course has
|
||||||
# "STYLE: Double" but no "STYLE: Single". To fix this, we copy over the P1
|
# "STYLE: Double" but no "STYLE: Single". To fix this, we copy over the P1
|
||||||
@ -181,7 +178,7 @@ def parse_tja_course_data(course):
|
|||||||
This provides a faithful, easy-to-inspect tree-style representation of the
|
This provides a faithful, easy-to-inspect tree-style representation of the
|
||||||
branches and measures within each course of the .tja file.
|
branches and measures within each course of the .tja file.
|
||||||
"""
|
"""
|
||||||
has_branches = bool([d for d in course.data if d.name == 'BRANCHSTART'])
|
has_branches = bool([d for d in course.data if d.startswith('#BRANCH')])
|
||||||
current_branch = 'all' if has_branches else 'normal'
|
current_branch = 'all' if has_branches else 'normal'
|
||||||
branch_condition = None
|
branch_condition = None
|
||||||
flag_levelhold = False
|
flag_levelhold = False
|
||||||
@ -190,9 +187,16 @@ def parse_tja_course_data(course):
|
|||||||
idx_m = 0
|
idx_m = 0
|
||||||
idx_m_branchstart = 0
|
idx_m_branchstart = 0
|
||||||
for idx_l, line in enumerate(course.data):
|
for idx_l, line in enumerate(course.data):
|
||||||
|
# 0. Check to see whether line is a command or note data
|
||||||
|
command, value, notes = None, None, None
|
||||||
|
match_command = re.match(r"^#([A-Z]+)(?:\s+(.+))?", line)
|
||||||
|
if match_command:
|
||||||
|
command, value = match_command.groups()
|
||||||
|
else:
|
||||||
|
notes = line # If not a command, then line must be note data
|
||||||
|
|
||||||
# 1. Parse measure notes
|
# 1. Parse measure notes
|
||||||
if line.name == 'NOTES':
|
if notes:
|
||||||
notes = line.value
|
|
||||||
# If measure has ended, then add notes to the current measure,
|
# If measure has ended, then add notes to the current measure,
|
||||||
# then start a new measure by incrementing idx_m
|
# then start a new measure by incrementing idx_m
|
||||||
if notes.endswith(','):
|
if notes.endswith(','):
|
||||||
@ -210,37 +214,37 @@ def parse_tja_course_data(course):
|
|||||||
course.branches[branch][idx_m].notes += notes
|
course.branches[branch][idx_m].notes += notes
|
||||||
|
|
||||||
# 2. Parse measure commands that produce an "event"
|
# 2. Parse measure commands that produce an "event"
|
||||||
elif line.name in ['GOGOSTART', 'GOGOEND', 'BARLINEON', 'BARLINEOFF',
|
elif command in ['GOGOSTART', 'GOGOEND', 'BARLINEON', 'BARLINEOFF',
|
||||||
'DELAY', 'SCROLL', 'BPMCHANGE', 'MEASURE',
|
'DELAY', 'SCROLL', 'BPMCHANGE', 'MEASURE',
|
||||||
'SECTION', 'BRANCHSTART']:
|
'SECTION', 'BRANCHSTART']:
|
||||||
# Get position of the event
|
# Get position of the event
|
||||||
for branch in (course.branches.keys() if current_branch == 'all'
|
for branch in (course.branches.keys() if current_branch == 'all'
|
||||||
else [current_branch]):
|
else [current_branch]):
|
||||||
pos = len(course.branches[branch][idx_m].notes)
|
pos = len(course.branches[branch][idx_m].notes)
|
||||||
|
|
||||||
# Parse event type
|
# Parse event type
|
||||||
if line.name == 'GOGOSTART':
|
if command == 'GOGOSTART':
|
||||||
current_event = TJAData('gogo', '1', pos)
|
current_event = TJAData('gogo', '1', pos)
|
||||||
elif line.name == 'GOGOEND':
|
elif command == 'GOGOEND':
|
||||||
current_event = TJAData('gogo', '0', pos)
|
current_event = TJAData('gogo', '0', pos)
|
||||||
elif line.name == 'BARLINEON':
|
elif command == 'BARLINEON':
|
||||||
current_event = TJAData('barline', '1', pos)
|
current_event = TJAData('barline', '1', pos)
|
||||||
elif line.name == 'BARLINEOFF':
|
elif command == 'BARLINEOFF':
|
||||||
current_event = TJAData('barline', '0', pos)
|
current_event = TJAData('barline', '0', pos)
|
||||||
elif line.name == 'DELAY':
|
elif command == 'DELAY':
|
||||||
current_event = TJAData('delay', float(line.value), pos)
|
current_event = TJAData('delay', float(value), pos)
|
||||||
elif line.name == 'SCROLL':
|
elif command == 'SCROLL':
|
||||||
current_event = TJAData('scroll', float(line.value), pos)
|
current_event = TJAData('scroll', float(value), pos)
|
||||||
elif line.name == 'BPMCHANGE':
|
elif command == 'BPMCHANGE':
|
||||||
current_event = TJAData('bpm', float(line.value), pos)
|
current_event = TJAData('bpm', float(value), pos)
|
||||||
elif line.name == 'MEASURE':
|
elif command == 'MEASURE':
|
||||||
current_event = TJAData('measure', line.value, pos)
|
current_event = TJAData('measure', value, pos)
|
||||||
elif line.name == 'SECTION':
|
elif command == 'SECTION':
|
||||||
# If #SECTION occurs before a #BRANCHSTART, then ensure that
|
# If #SECTION occurs before a #BRANCHSTART, then ensure that
|
||||||
# it's present on every branch. Otherwise, #SECTION will only
|
# it's present on every branch. Otherwise, #SECTION will only
|
||||||
# be present on the current branch, and so the `branch_info`
|
# be present on the current branch, and so the `branch_info`
|
||||||
# values won't be correctly set for the other two branches.
|
# values won't be correctly set for the other two branches.
|
||||||
if course.data[idx_l+1].name == 'BRANCHSTART':
|
if course.data[idx_l+1].startswith('#BRANCHSTART'):
|
||||||
current_event = TJAData('section', None, pos)
|
current_event = TJAData('section', None, pos)
|
||||||
current_branch = 'all'
|
current_branch = 'all'
|
||||||
# Otherwise, #SECTION exists in isolation. In this case, to
|
# Otherwise, #SECTION exists in isolation. In this case, to
|
||||||
@ -248,12 +252,12 @@ def parse_tja_course_data(course):
|
|||||||
else:
|
else:
|
||||||
current_event = TJAData('branch_start', branch_condition,
|
current_event = TJAData('branch_start', branch_condition,
|
||||||
pos)
|
pos)
|
||||||
elif line.name == 'BRANCHSTART':
|
elif command == 'BRANCHSTART':
|
||||||
if flag_levelhold:
|
if flag_levelhold:
|
||||||
continue
|
continue
|
||||||
# Ensure that the #BRANCHSTART command is added to all branches
|
# Ensure that the #BRANCHSTART command is added to all branches
|
||||||
current_branch = 'all'
|
current_branch = 'all'
|
||||||
branch_condition = line.value.split(',')
|
branch_condition = value.split(',')
|
||||||
if branch_condition[0] == 'r': # r = drumRoll
|
if branch_condition[0] == 'r': # r = drumRoll
|
||||||
branch_condition[1] = int(branch_condition[1]) # drumrolls
|
branch_condition[1] = int(branch_condition[1]) # drumrolls
|
||||||
branch_condition[2] = int(branch_condition[2]) # drumrolls
|
branch_condition[2] = int(branch_condition[2]) # drumrolls
|
||||||
@ -272,25 +276,25 @@ def parse_tja_course_data(course):
|
|||||||
# 3. Parse commands that don't create an event
|
# 3. Parse commands that don't create an event
|
||||||
# (e.g. simply changing the current branch)
|
# (e.g. simply changing the current branch)
|
||||||
else:
|
else:
|
||||||
if line.name == 'START' or line.name == 'END':
|
if command == 'START' or command == 'END':
|
||||||
current_branch = 'all' if has_branches else 'normal'
|
current_branch = 'all' if has_branches else 'normal'
|
||||||
flag_levelhold = False
|
flag_levelhold = False
|
||||||
elif line.name == 'LEVELHOLD':
|
elif command == 'LEVELHOLD':
|
||||||
flag_levelhold = True
|
flag_levelhold = True
|
||||||
elif line.name == 'N':
|
elif command == 'N':
|
||||||
current_branch = 'normal'
|
current_branch = 'normal'
|
||||||
idx_m = idx_m_branchstart
|
idx_m = idx_m_branchstart
|
||||||
elif line.name == 'E':
|
elif command == 'E':
|
||||||
current_branch = 'professional'
|
current_branch = 'professional'
|
||||||
idx_m = idx_m_branchstart
|
idx_m = idx_m_branchstart
|
||||||
elif line.name == 'M':
|
elif command == 'M':
|
||||||
current_branch = 'master'
|
current_branch = 'master'
|
||||||
idx_m = idx_m_branchstart
|
idx_m = idx_m_branchstart
|
||||||
elif line.name == 'BRANCHEND':
|
elif command == 'BRANCHEND':
|
||||||
current_branch = 'all'
|
current_branch = 'all'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"Ignoring unsupported command '{line.name}'")
|
print(f"Ignoring unsupported command '{command}'")
|
||||||
|
|
||||||
# Delete the last measure in the branch if no notes or events
|
# Delete the last measure in the branch if no notes or events
|
||||||
# were added to it (due to preallocating empty measures)
|
# were added to it (due to preallocating empty measures)
|
||||||
|
Loading…
Reference in New Issue
Block a user