CORE/preprocessor.gd

365 lines
16 KiB
GDScript3
Raw Normal View History

2023-03-18 16:34:12 +01:00
# preprocessor.gd
# CORE Text Preprocessor
#
# This file is part of StarOpenSource CORE (SOSCORE)
# Made by the StarOpenSource Project and Contributers
# Licensed under GNU GPLv3
#
# <tag> list/table:
# Tag | Description
# <nl> | Newline (DO NOT USE '\n'!)
# <lo> | Text is lowercase until closing tag
# <up> | Text is UPPERCASE until closing tag
# <ca> | Text is camelCase until closing tag
# <pa> | Text is PascalCase until closing tag
# <sn> | Text is snake_case until closing tag
# </lo> | Closing tag for <lo>
# </up> | Closing tag for <up>
# </ca> | Closing tag for <ca>
# </pa> | Closing tag for <pa>
# </sn> | Closing tag for <sn>
extends Control
var logger = null
@export_category("Configuration")
# Enables/disables Preprocessor.
@export var enabled = true
# Enables/disables verbose preprocessing. DO NOT ENABLE. VERBOSE PREPROCESSING ADDS 20 LINES FOR EACH LOGGER CALL WITH PREPROCESSING ENABLED AND MAKES THE LOG UNREADABLE. ONLY ENABLE IF YOU KNOW WHAT YOU ARE DOING.
# Requires Logger.enable -> true
@export var verbose_logging = false
# Enables/disables Preprocessor tests. Please do not disable as testing the preprocessor can help catch bugs early.
# Requires enabled -> true
@export var tests_enabled = true
# Controls how test results are displayed
# 0 = Never show results
# 1 = Only display on failure
# 2 = Always display
@export var tests_level = 1
2023-03-18 16:34:12 +01:00
var tests_executed = false
var tests_log = ""
var tests_success = 0
func _ready() -> void:
print("preprocessor -> Initializing Preprocessor")
print("preprocessor -> Delaying initialization by 0.75s")
await get_tree().create_timer(0.75).timeout
logger = get_node(NodePath("/root/Logger"))
await get_tree().create_timer(0.05).timeout
do_tests(false)
if enabled:
logger.diag("Preprocessor","Preprocessor ready.",false)
2023-03-18 16:34:12 +01:00
else:
logger.diag("Preprocessor","Preprocessor disabled.",false)
2023-03-18 16:34:12 +01:00
# Performs tests on all Preprocessor features to help catch bugs while debugging.
# Recommended to leave enabled because log output might look weird if the a faulty commit is installed.
# Use tests_executed to check if the tests have been executed, use tests_log for the test log and
# read tests_success if you want the number of successful tests.
func do_tests(ignore_flag:bool = false) -> void:
logger.diag("Preprocessor","Testing preprocessor...",false)
2023-03-18 16:34:12 +01:00
# If tests are disaled and ignore_flag is false return
if !tests_enabled and !ignore_flag:
logger.diag("Preprocessor","Testing failed: Tests are disabled.",false)
2023-03-18 16:34:12 +01:00
return
# If the Preprocessor is not enabled return
if !enabled:
logger.diag("Preprocessor","Testing failed: Preprocessor is disabled.",false)
2023-03-18 16:34:12 +01:00
return
# Expected test resulsts
var tests_newlines = "test_prefix_Line 1.\n _Line 2.\n _Line 3."
2023-03-18 16:34:12 +01:00
var tests_uppercase = "a THIS TEXT SHOULD BE UPPPERCASED b"
var tests_lowercase = "a this text should be lowercased b"
var tests_camelcase = "a thisShouldBeCamelCasEd b"
var tests_pascalcase = "a ThisShouldBePascalCasEd b"
var tests_snakecase = "a this_should_be_snake_cas_ed b"
var tests_escapes_newlines = "<nl>"
var tests_escapes_uppercase = "<up></up>"
var tests_escapes_lowercase = "<lo></lo>"
var tests_escapes_camelcase = "<ca></ca>"
var tests_escapes_pascalcase = "<pa></pa>"
var tests_escapes_snakecase = "<sn></sn>"
# Actual tests performed using pre()
var tests_newlines_result = process("Line 1<nl>Line 2<nl>Line 3","test_prefix","_",".")
var tests_uppercase_result = process("a <up>ThIs tExT sHoUlD bE uPpPeRcAsEd</up> b")
var tests_lowercase_result = process("a <lo>ThIs tExT sHoUlD bE lOwErCaSeD</lo> b")
var tests_camelcase_result = process("a <ca>thisShould_be_camel_CASEd</ca> b")
var tests_pascalcase_result = process("a <pa>thisShould_be_pascal_CASEd</pa> b")
var tests_snakecase_result = process("a <sn>thisShould_be_snake_CASEd</sn> b")
var tests_escapes_newlines_result = process("<esc_nl>")
var tests_escapes_uppercase_result = process("<esc_up></esc_up>")
var tests_escapes_lowercase_result = process("<esc_lo></esc_lo>")
var tests_escapes_camelcase_result = process("<esc_ca></esc_ca>")
var tests_escapes_pascalcase_result = process("<esc_pa></esc_pa>")
var tests_escapes_snakecase_result = process("<esc_sn></esc_sn>")
2023-03-18 16:34:12 +01:00
# Test success and log
tests_success = 0
tests_log = ""
# Checks if all tests are equal to their expected results and logs the result.
if tests_newlines_result == tests_newlines:
tests_success = tests_success+1
tests_log = "Y Test NEWLINES returned:\n" + tests_newlines_result + "\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = "N Test NEWLINES returned:\n" + tests_newlines_result + "\n... instead of:\n" + tests_newlines + "\n"
2023-03-18 16:34:12 +01:00
if tests_uppercase_result == tests_uppercase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test UPPERCASE returned \"" + tests_uppercase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test UPPERCASE returned \"" + tests_uppercase_result + "\" instead of \"\n" + tests_uppercase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_lowercase_result == tests_lowercase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test LOWERCASE returned \"" + tests_lowercase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test LOWERCASE returned \"" + tests_lowercase_result + "\" instead of \"\n" + tests_lowercase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_camelcase_result == tests_camelcase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test CAMELCASE returned \"" + tests_camelcase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test CAMELCASE returned \"" + tests_camelcase_result + "\" instead of \"\n" + tests_camelcase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_pascalcase_result == tests_pascalcase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test PASCALCASE returned \"" + tests_pascalcase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test PASCALCASE returned \"" + tests_pascalcase_result + "\" instead of \"\n" + tests_pascalcase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_snakecase_result == tests_snakecase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test SNAKECASE returned \"" + tests_snakecase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test SNAKECASE returned \"" + tests_snakecase_result + "\" instead of \"\n" + tests_snakecase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_escapes_newlines_result == tests_escapes_newlines:
tests_success = tests_success+1
tests_log = tests_log + "Y Test ESCAPES_NEWLINES returned \"" + tests_escapes_newlines_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test ESCAPES_NEWLINES returned \"" + tests_escapes_newlines_result + "\" instead of \"" + tests_escapes_newlines + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_escapes_uppercase_result == tests_escapes_uppercase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test ESCAPES_UPPERCASE returned \"" + tests_escapes_uppercase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test ESCAPES_UPPERCASE returned \"" + tests_escapes_uppercase_result + "\" instead of \"" + tests_escapes_uppercase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_escapes_lowercase_result == tests_escapes_lowercase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test ESCAPES_LOWERCASE returned \"" + tests_escapes_lowercase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test ESCAPES_LOWERCASE returned \"" + tests_escapes_lowercase_result + "\" instead of \"" + tests_escapes_lowercase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_escapes_camelcase_result == tests_escapes_camelcase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test ESCAPES_CAMELCASE returned \"" + tests_escapes_camelcase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test ESCAPES_CAMELCASE returned \"" + tests_escapes_camelcase_result + "\" instead of \"" + tests_escapes_camelcase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_escapes_pascalcase_result == tests_escapes_pascalcase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test ESCAPES_PASCALCASE returned \"" + tests_escapes_pascalcase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test ESCAPES_PASCALCASE returned \"" + tests_escapes_pascalcase_result + "\" instead of \"" + tests_escapes_pascalcase + "\".\n"
2023-03-18 16:34:12 +01:00
if tests_escapes_snakecase_result == tests_escapes_snakecase:
tests_success = tests_success+1
tests_log = tests_log + "Y Test ESCAPES_SNAKECASE returned \"" + tests_escapes_snakecase_result + "\".\n"
2023-03-18 16:34:12 +01:00
else:
tests_log = tests_log + "N Test ESCAPES_SNAKECASE returned \"" + tests_escapes_snakecase_result + "\" instead of \"." + tests_escapes_snakecase + "\".\n"
2023-03-18 16:34:12 +01:00
tests_log = tests_log.left(tests_log.length()-1) # Removes unnecessary line.
tests_executed = true
# Checks if all tests were successful. If not disable the Preprocessor and print log.
if tests_success == 12:
enabled = true
logger.diag("Preprocessor","Testing complete: All preprocessor features are working as intended.",false)
if tests_level == 2:
logger.diag("Preprocessor","Log:\n" + tests_log)
2023-03-18 16:34:12 +01:00
else:
enabled = false
logger.error("Preprocessor","Testing failed: Preprocessor is malfunctioning and is now disabled.",false)
if tests_level >= 1:
logger.error("Preprocessor","Log:\n" + tests_log)
2023-03-18 16:34:12 +01:00
# Processes <nl> tags
func _newline(msg:String,prefix:String,pre_msg:String,post_msg:String,exclusion_filter:Array) -> String:
2023-03-18 16:34:12 +01:00
if verbose_logging:
logger.diag("Preprocessor","Preprocessing newlines")
var prefix_space = prefix
for exclusion in exclusion_filter:
prefix_space = prefix_space.replace(exclusion,"")
prefix_space = " ".repeat(prefix_space.length())
var msg_proc = prefix + pre_msg + msg.replace("<nl>",post_msg + "\n" + prefix_space + pre_msg) + post_msg
2023-03-18 16:34:12 +01:00
if verbose_logging:
logger.diag("Preprocessor","Preprocessed newlines")
return msg_proc
# Processes <lo> and </lo> tags
# Warning: Please do NOT modify the while true loop. It was a pain to debug, believe me.
func _case_lower(msg:String) -> String:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing lowercasing")
var msg_proc = msg
while true:
var index_one = msg_proc.find("<lo>")
var index_two = msg_proc.find("</lo>") + 3
if index_one != -1:
if index_two != -1:
var index_proc = msg_proc.substr(index_one,index_two)
msg_proc = msg_proc.replace(index_proc,index_proc.replace("<lo>","").replace("</lo>","").to_lower())
else:
logger.error("Preprocessor","Failed preprocessing message \"" + msg + "\" (stage LOWERCASE).",false)
msg_proc = msg
break
else:
break
if verbose_logging:
logger.diag("Preprocessor","Preprocessed lowercasing")
return msg_proc
# Processes <up> and </up> tags
# Warning: Please do NOT modify the while true loop. It was a pain to debug, believe me.
func _case_upper(msg:String) -> String:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing uppercasing")
var msg_proc = msg
while true:
var index_one = msg_proc.find("<up>")
var index_two = msg_proc.find("</up>") + 3
if index_one != -1:
if index_two != -1:
var index_proc = msg_proc.substr(index_one,index_two)
msg_proc = msg_proc.replace(index_proc,index_proc.replace("<up>","").replace("</up>","").to_upper())
else:
logger.error("Preprocessor","Failed preprocessing message \"" + msg + "\". (stage UPPERCASE).",false)
msg_proc = msg
break
else:
break
if verbose_logging:
logger.diag("Preprocessor","Preprocessed uppercasing")
return msg_proc
# Processes <ca> and </ca> tags
# Warning: Please do NOT modify the while true loop. It was a pain to debug, believe me.
func _case_camelcase(msg:String) -> String:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing camelcasing")
var msg_proc = msg
while true:
var index_one = msg_proc.find("<ca>")
var index_two = msg_proc.find("</ca>") + 3
if index_one != -1:
if index_two != -1:
var index_proc = msg_proc.substr(index_one,index_two)
msg_proc = msg_proc.replace(index_proc,index_proc.replace("<ca>","").replace("</ca>","").to_camel_case())
else:
logger.error("Preprocessor","Failed preprocessing message \"" + msg + "\". (stage CAMELCASE).",false)
msg_proc = msg
break
else:
break
if verbose_logging:
logger.diag("Preprocessor","Preprocessed camelcasing")
return msg_proc
# Processes <pa> and </pa> tags
# Warning: Please do NOT modify the while true loop. It was a pain to debug, believe me.
func _case_pascalcase(msg:String) -> String:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing pascalcasing")
var msg_proc = msg
while true:
var index_one = msg_proc.find("<pa>")
var index_two = msg_proc.find("</pa>") + 3
if index_one != -1:
if index_two != -1:
var index_proc = msg_proc.substr(index_one,index_two)
msg_proc = msg_proc.replace(index_proc,index_proc.replace("<pa>","").replace("</pa>","").to_pascal_case())
else:
logger.error("Preprocessor","Failed preprocessing message \"" + msg + "\". (stage PASCALCASE).",false)
msg_proc = msg
break
else:
break
if verbose_logging:
logger.diag("Preprocessor","Preprocessed pascalcasing")
return msg_proc
# Processes <sn> and </sn> tags
# Warning: Please do NOT modify the while true loop. It was a pain to debug, believe me.
func _case_snakecase(msg:String) -> String:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing snakecasing")
var msg_proc = msg
while true:
var index_one = msg_proc.find("<sn>")
var index_two = msg_proc.find("</sn>") + 3
if index_one != -1:
if index_two != -1:
var index_proc = msg_proc.substr(index_one,index_two)
msg_proc = msg_proc.replace(index_proc,index_proc.replace("<sn>","").replace("</sn>","").to_snake_case())
else:
logger.error("Preprocessor","Failed preprocessing message \"" + msg + "\". (stage SNAKECASE).",false)
msg_proc = msg
break
else:
break
if verbose_logging:
logger.diag("Preprocessor","Preprocessed snakecasing")
return msg_proc
# Functions to call all other functions related to *case
func _case(msg:String) -> String:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing casing")
var msg_proc = _case_lower(msg)
msg_proc = _case_upper(msg_proc)
msg_proc = _case_camelcase(msg_proc)
msg_proc = _case_pascalcase(msg_proc)
msg_proc = _case_snakecase(msg_proc)
if verbose_logging:
logger.diag("Preprocessor","Preprocessed casing")
return msg_proc
# Replaces all escapes
func _escapes(msg:String) -> String:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing escapes")
var msg_proc = msg.replace("<esc_nl>","<nl>")
msg_proc = msg_proc.replace("<esc_up>","<up>")
msg_proc = msg_proc.replace("<esc_lo>","<lo>")
msg_proc = msg_proc.replace("<esc_ca>","<ca>")
msg_proc = msg_proc.replace("<esc_pa>","<pa>")
msg_proc = msg_proc.replace("<esc_sn>","<sn>")
msg_proc = msg_proc.replace("</esc_up>","</up>")
msg_proc = msg_proc.replace("</esc_lo>","</lo>")
msg_proc = msg_proc.replace("</esc_ca>","</ca>")
msg_proc = msg_proc.replace("</esc_pa>","</pa>")
msg_proc = msg_proc.replace("</esc_sn>","</sn>")
msg_proc = msg_proc.replace("<esc_np>","<np>")
if verbose_logging:
logger.diag("Preprocessor","Preprocessed escapes")
return msg_proc
# Checks for <np> and if found stops preprocessing
func _noprocess(msg:String) -> String:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing noprocess")
if msg.find("<np>") != -1:
logger.diag("Preprocessor","Preprocessed noprocess")
return msg.replace("<np>","")
if verbose_logging:
logger.diag("Preprocessor","Preprocessed noprocess")
return msg
# Executes all preprocessing functions and returns their combined work
func process(msg:String,prefix:String = "",pre_msg:String = "",post_msg:String = "",exclusion_filter:Array = []) -> String:
2023-03-18 16:34:12 +01:00
if verbose_logging:
logger.diag("Preprocessor","Preprocessing a message")
2023-03-18 16:34:12 +01:00
if !enabled:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing failed: Preprocessor is disabled")
return msg
# _noprocess() returns a modified msg variable with <np> removed. That's returned and then checked against the "real" msg variable.
var msg_proc = _noprocess(msg)
if msg_proc != msg:
if verbose_logging:
logger.diag("Preprocessor","Preprocessing cancelled: <np> tag detected")
return msg_proc
msg_proc = _case(msg)
msg_proc = _newline(msg_proc,prefix,pre_msg,post_msg,exclusion_filter)
2023-03-18 16:34:12 +01:00
msg_proc = _escapes(msg_proc)
if verbose_logging:
logger.diag("Preprocessor","Preprocessing complete")
return msg_proc