mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 13:10:02 +01:00
Fixes 4 bugs with /datum/parsed_map/readlist() (#92652)
## About The Pull Request - Fixes the proc skipping `0` & `null` values. Text like `"0"` & `"null"` when sent to `parse_constant()` will yield `0` & `null` respectively but when checked against `!` operator https://github.com/tgstation/tgstation/blob/c3e716323e3bbcfdd8704def76791664a34b8adc/code/modules/mapping/reader.dm#L1019 It gets mistaken as an empty value/new line so its skipped So if you had a list such as `list("Hello", 0, "null")` When parsed it would yield `list("Hello")`. That's fixed now. We check the text before parsing it - Fixes `=` symbol getting mistaken for a key/value pair if it is embedded inside a string expression. Consider this list `list("A", "B = C")`. This list should just be linear because the equals symbol is meant to escaped as it is within a string literal. However `findtext()` proc doesn't account for that https://github.com/tgstation/tgstation/blob/c3e716323e3bbcfdd8704def76791664a34b8adc/code/modules/mapping/reader.dm#L1014 So we end up creating a key/value pair and a linear element, creating a broken list like that runtimes(because of the missing `"` symbol in breaking up the string at the middle) The solution is simple, we already have the proc `find_next_delimiter_position()` that will escape everything between `"` so we now use that to find the position of `=` - Fixes the proc being unable to parse alists who's values themselves are lists. Consider the example `list("A" = list(1, 2))` When we look for `,` symbol to check for the next list element here https://github.com/tgstation/tgstation/blob/c3e716323e3bbcfdd8704def76791664a34b8adc/code/modules/mapping/reader.dm#L1015 You get a broken left literal like this `"A" = list(1` because the comma within the list is getting mistaken for a new element. Now we check for such edge cases and remember to parse the full list before checking for the next comma & now your alists can contain lists as elements themselves - Map reader can read nested lists to any degree e.g. `list(list(3, 4), list(5, 6))` parses correctly ## Changelog 🆑 fix: the map reader now reads null & 0 values into lists fix: the map reader now parses associative lists(maps/alists) correctly in cases where = sign is embedded within a string fix: the map reader now parses associative lists(maps/alists) who's values themselves can be lists fix: the map reader can now parse nested lists to any degree /🆑
This commit is contained in:
@@ -1008,27 +1008,61 @@ GLOBAL_LIST_EMPTY(map_model_default)
|
||||
var/old_position = 1
|
||||
while(position != 0)
|
||||
// find next delimiter that is not within "..."
|
||||
position = find_next_delimiter_position(text,old_position,delimiter)
|
||||
position = find_next_delimiter_position(text, old_position, delimiter)
|
||||
|
||||
// check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7))
|
||||
var/equal_position = findtext(text,"=",old_position, position)
|
||||
var/trim_left = trim(copytext(text,old_position,(equal_position ? equal_position : position)))
|
||||
var/left_constant = parse_constant(trim_left)
|
||||
if(position)
|
||||
old_position = position + length(text[position])
|
||||
if(!left_constant) // damn newlines man. Exists to provide behavior consistency with the above loop. not a major cost becuase this path is cold
|
||||
var/equal_position = find_next_delimiter_position(text, old_position, "=")
|
||||
var/trim_left = trim(copytext(text, old_position, (equal_position ? equal_position : position)))
|
||||
if(!trim_left) // damn newlines man. Exists to provide behavior consistency with the above loop. not a major cost becuase this path is cold
|
||||
if(position)
|
||||
old_position = position + length(text[position])
|
||||
continue
|
||||
|
||||
if(equal_position && !isnum(left_constant))
|
||||
var/is_simple = TRUE //linear list
|
||||
var/trim_right = trim_left //simple var
|
||||
if(equal_position)
|
||||
// Associative var, so do the association.
|
||||
// Note that numbers cannot be keys - the RHS is dropped if so.
|
||||
var/trim_right = trim(copytext(text, equal_position + length(text[equal_position]), position))
|
||||
var/right_constant = parse_constant(trim_right)
|
||||
.[left_constant] = right_constant
|
||||
else // simple var
|
||||
trim_right = trim(copytext(text, equal_position + length(text[equal_position]), position))
|
||||
is_simple = FALSE
|
||||
|
||||
//right value is a list and since we used the delimiter , this text would be incomplete so we need to parse the full string
|
||||
if(copytext(trim_right, 1, 6) == "list(")
|
||||
var/start_index = is_simple ? old_position : equal_position + length(text[equal_position])
|
||||
var/opening_count = 0
|
||||
var/closing_count = 0
|
||||
var/index = start_index
|
||||
var/begin = FALSE
|
||||
while(!begin || (opening_count != closing_count))
|
||||
var/char = text[index]
|
||||
if(char == "(")
|
||||
opening_count += 1
|
||||
begin = TRUE
|
||||
else if(char == ")")
|
||||
closing_count += 1
|
||||
index += 1
|
||||
trim_right = trim(copytext(text, start_index, index))
|
||||
if(is_simple)
|
||||
trim_left = trim_right
|
||||
if(index == length(text)) //stops a wasteful iteration when we reach the end
|
||||
position = 0
|
||||
else
|
||||
old_position = index + 1 //this moves our pointer past , to the next element
|
||||
else if(position)
|
||||
old_position = position + length(text[position])
|
||||
|
||||
//assign value
|
||||
var/left_constant = parse_constant(trim_left)
|
||||
if(is_simple)
|
||||
. += list(left_constant)
|
||||
else
|
||||
.[left_constant] = parse_constant(trim_right)
|
||||
|
||||
/datum/parsed_map/proc/parse_constant(text)
|
||||
// empty text
|
||||
if(!text)
|
||||
return ""
|
||||
|
||||
// number
|
||||
var/num = text2num(text)
|
||||
if(isnum(num))
|
||||
|
||||
Reference in New Issue
Block a user